Introduce BaseExpressionCodegen and SourceCompilerForInline,

switch CallGenerator and InlineCodegen to them

~base
This commit is contained in:
Mikhael Bogdanov
2017-06-12 15:20:20 +02:00
parent 3ae214b97a
commit 8dc1b8f95f
7 changed files with 501 additions and 350 deletions
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.inline.NameGenerator
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
interface BaseExpressionCodegen {
val frameMap: FrameMap
val visitor: InstructionAdapter
val inlineNameGenerator: NameGenerator
val lastLineNumber: Int
fun consumeReifiedOperationMarker(typeParameterDescriptor: TypeParameterDescriptor)
fun propagateChildReifiedTypeParametersUsages(reifiedTypeParametersUsages: ReifiedTypeParametersUsages)
fun pushClosureOnStack(
classDescriptor: ClassDescriptor,
putThis: Boolean,
callGenerator: CallGenerator,
functionReferenceReceiver: StackValue?
)
fun markLineNumberAfterInlineIfNeeded()
}
@@ -118,7 +118,7 @@ import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFun
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral; import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral;
import static org.jetbrains.org.objectweb.asm.Opcodes.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> implements LocalLookup { public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> implements LocalLookup, BaseExpressionCodegen {
private final GenerationState state; private final GenerationState state;
final KotlinTypeMapper typeMapper; final KotlinTypeMapper typeMapper;
private final BindingContext bindingContext; private final BindingContext bindingContext;
@@ -1352,6 +1352,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
} }
//we should generate additional linenumber info after inline call only if it used as argument //we should generate additional linenumber info after inline call only if it used as argument
@Override
public void markLineNumberAfterInlineIfNeeded() { public void markLineNumberAfterInlineIfNeeded() {
if (!shouldMarkLineNumbers) { if (!shouldMarkLineNumbers) {
//if it used as general argument //if it used as general argument
@@ -1366,6 +1367,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
} }
} }
@Override
public int getLastLineNumber() { public int getLastLineNumber() {
return myLastLineNumber; return myLastLineNumber;
} }
@@ -2286,10 +2288,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
FunctionDescriptor original = FunctionDescriptor original =
unwrapInitialSignatureDescriptor(DescriptorUtils.unwrapFakeOverride((FunctionDescriptor) descriptor.getOriginal())); unwrapInitialSignatureDescriptor(DescriptorUtils.unwrapFakeOverride((FunctionDescriptor) descriptor.getOriginal()));
if (isDefaultCompilation) { if (isDefaultCompilation) {
return new InlineCodegenForDefaultBody(original, this, state); return new InlineCodegenForDefaultBody(original, this, state, new SourceCompilerForInline(this));
} }
else { else {
return new InlineCodegen(this, state, original, callElement, typeParameterMappings); return new InlineCodegen(this, state, original, callElement, typeParameterMappings, new SourceCompilerForInline(this));
} }
} }
@@ -4060,19 +4062,17 @@ The "returned" value of try expression with no finally is either the last expres
public void putReifiedOperationMarkerIfTypeIsReifiedParameter( public void putReifiedOperationMarkerIfTypeIsReifiedParameter(
@NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind @NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind
) { ) {
putReifiedOperationMarkerIfTypeIsReifiedParameter(type, operationKind, v); putReifiedOperationMarkerIfTypeIsReifiedParameter(type, operationKind, v, this);
} }
public void putReifiedOperationMarkerIfTypeIsReifiedParameter( public static void putReifiedOperationMarkerIfTypeIsReifiedParameter(
@NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind, @NotNull InstructionAdapter v @NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind, @NotNull InstructionAdapter v,
@NotNull BaseExpressionCodegen codegen
) { ) {
Pair<TypeParameterDescriptor, ReificationArgument> typeParameterAndReificationArgument = extractReificationArgument(type); Pair<TypeParameterDescriptor, ReificationArgument> typeParameterAndReificationArgument = extractReificationArgument(type);
if (typeParameterAndReificationArgument != null && typeParameterAndReificationArgument.getFirst().isReified()) { if (typeParameterAndReificationArgument != null && typeParameterAndReificationArgument.getFirst().isReified()) {
TypeParameterDescriptor typeParameterDescriptor = typeParameterAndReificationArgument.getFirst(); TypeParameterDescriptor typeParameterDescriptor = typeParameterAndReificationArgument.getFirst();
if (typeParameterDescriptor.getContainingDeclaration() != context.getContextDescriptor()) { codegen.consumeReifiedOperationMarker(typeParameterDescriptor);
parentCodegen.getReifiedTypeParametersUsages().
addUsedReifiedParameter(typeParameterDescriptor.getName().asString());
}
v.iconst(operationKind.getId()); v.iconst(operationKind.getId());
v.visitLdcInsn(typeParameterAndReificationArgument.getSecond().asString()); v.visitLdcInsn(typeParameterAndReificationArgument.getSecond().asString());
v.invokestatic( v.invokestatic(
@@ -4082,6 +4082,7 @@ The "returned" value of try expression with no finally is either the last expres
} }
} }
@Override
public void propagateChildReifiedTypeParametersUsages(@NotNull ReifiedTypeParametersUsages usages) { public void propagateChildReifiedTypeParametersUsages(@NotNull ReifiedTypeParametersUsages usages) {
parentCodegen.getReifiedTypeParametersUsages().propagateChildUsagesWithinContext(usages, context); parentCodegen.getReifiedTypeParametersUsages().propagateChildUsagesWithinContext(usages, context);
} }
@@ -4203,6 +4204,7 @@ The "returned" value of try expression with no finally is either the last expres
return context.getContextDescriptor().toString(); return context.getContextDescriptor().toString();
} }
@Override
@NotNull @NotNull
public FrameMap getFrameMap() { public FrameMap getFrameMap() {
return myFrameMap; return myFrameMap;
@@ -4213,6 +4215,7 @@ The "returned" value of try expression with no finally is either the last expres
return context; return context;
} }
@Override
@NotNull @NotNull
public NameGenerator getInlineNameGenerator() { public NameGenerator getInlineNameGenerator() {
NameGenerator nameGenerator = getParentCodegen().getInlineNameGenerator(); NameGenerator nameGenerator = getParentCodegen().getInlineNameGenerator();
@@ -4255,4 +4258,18 @@ The "returned" value of try expression with no finally is either the last expres
) { ) {
return StackValue.delegate(typeMapper.mapType(variableDescriptor.getType()), delegateValue, metadataValue, variableDescriptor, this); return StackValue.delegate(typeMapper.mapType(variableDescriptor.getType()), delegateValue, metadataValue, variableDescriptor, this);
} }
@NotNull
@Override
public InstructionAdapter getVisitor() {
return v;
}
@Override
public void consumeReifiedOperationMarker(@NotNull TypeParameterDescriptor typeParameterDescriptor) {
if (typeParameterDescriptor.getContainingDeclaration() != context.getContextDescriptor()) {
parentCodegen.getReifiedTypeParametersUsages().
addUsedReifiedParameter(typeParameterDescriptor.getName().asString());
}
}
} }
@@ -18,12 +18,10 @@ package org.jetbrains.kotlin.codegen.inline
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.util.ArrayUtil import com.intellij.util.ArrayUtil
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment
import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags import org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
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.intrinsics.bytecode import org.jetbrains.kotlin.codegen.intrinsics.bytecode
@@ -39,11 +37,11 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.resolve.inline.InlineUtil.isInlinableParameterExpression import org.jetbrains.kotlin.resolve.inline.InlineUtil.isInlinableParameterExpression
import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.MemberScope
@@ -51,22 +49,21 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral
import org.jetbrains.kotlin.types.expressions.LabelResolver import org.jetbrains.kotlin.types.expressions.LabelResolver
import org.jetbrains.org.objectweb.asm.Label
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.Type import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.commons.Method import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.LabelNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode import org.jetbrains.org.objectweb.asm.tree.MethodNode
import java.util.* import java.util.*
class InlineCodegen( class InlineCodegen(
private val codegen: ExpressionCodegen, private val codegen: BaseExpressionCodegen,
private val state: GenerationState, private val state: GenerationState,
function: FunctionDescriptor, function: FunctionDescriptor,
private val callElement: KtElement, private val callElement: KtElement,
private val typeParameterMappings: TypeParameterMappings private val typeParameterMappings: TypeParameterMappings,
private val sourceCompiler: SourceCompilerForInline
) : CallGenerator() { ) : CallGenerator() {
init { init {
assert(InlineUtil.isInline(function) || InlineUtil.isArrayConstructorWithLambda(function)) { assert(InlineUtil.isInline(function) || InlineUtil.isArrayConstructorWithLambda(function)) {
@@ -89,15 +86,9 @@ class InlineCodegen(
else else
function.original function.original
private val context = private val jvmSignature: JvmMethodGenericSignature
getContext(
functionDescriptor, state,
DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)?.containingFile as? KtFile
) as MethodContext
private val jvmSignature = typeMapper.mapSignatureWithGeneric(functionDescriptor, context.contextKind) private val isSameModule: Boolean
private val isSameModule = JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), state.outDirectory)
private val invocationParamBuilder = ParametersBuilder.newBuilder() private val invocationParamBuilder = ParametersBuilder.newBuilder()
@@ -105,7 +96,7 @@ class InlineCodegen(
private var activeLambda: LambdaInfo? = null private var activeLambda: LambdaInfo? = null
private val sourceMapper = codegen.parentCodegen.orCreateSourceMapper private val sourceMapper = sourceCompiler.lazySourceMapper
private var delayedHiddenWriting: Function0<Unit>? = null private var delayedHiddenWriting: Function0<Unit>? = null
@@ -114,8 +105,12 @@ class InlineCodegen(
private var methodHandleInDefaultMethodIndex = -1 private var methodHandleInDefaultMethodIndex = -1
init { init {
sourceCompiler.initializeInlineFunctionContext(functionDescriptor)
jvmSignature = typeMapper.mapSignatureWithGeneric(functionDescriptor, sourceCompiler.contextKind)
isSameModule = sourceCompiler.isCallInsideSameModuleAsDeclared(functionDescriptor)
if (functionDescriptor !is FictitiousArrayConstructor) { if (functionDescriptor !is FictitiousArrayConstructor) {
reportIncrementalInfo(functionDescriptor, codegen.getContext().functionDescriptor.original, jvmSignature, state) reportIncrementalInfo(functionDescriptor, sourceCompiler.compilationContextFunctionDescriptor.original, jvmSignature, state)
val functionOrAccessorName = typeMapper.mapAsmMethod(function).name val functionOrAccessorName = typeMapper.mapAsmMethod(function).name
//track changes for property accessor and @JvmName inline functions/property accessors //track changes for property accessor and @JvmName inline functions/property accessors
if (functionOrAccessorName != functionDescriptor.name.asString()) { if (functionOrAccessorName != functionDescriptor.name.asString()) {
@@ -130,7 +125,7 @@ class InlineCodegen(
callableMethod: Callable, callableMethod: Callable,
resolvedCall: ResolvedCall<*>?, resolvedCall: ResolvedCall<*>?,
callDefault: Boolean, callDefault: Boolean,
codegen: ExpressionCodegen codegen: BaseExpressionCodegen
) { ) {
if (!state.inlineCycleReporter.enterIntoInlining(resolvedCall)) { if (!state.inlineCycleReporter.enterIntoInlining(resolvedCall)) {
generateStub(resolvedCall, codegen) generateStub(resolvedCall, codegen)
@@ -139,7 +134,7 @@ class InlineCodegen(
var nodeAndSmap: SMAPAndMethodNode? = null var nodeAndSmap: SMAPAndMethodNode? = null
try { try {
nodeAndSmap = createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault, resolvedCall) nodeAndSmap = createInlineMethodNode(functionDescriptor, jvmSignature, codegen, callDefault, resolvedCall, state, sourceCompiler)
endCall(inlineCall(nodeAndSmap, callDefault)) endCall(inlineCall(nodeAndSmap, callDefault))
} }
catch (e: CompilationException) { catch (e: CompilationException) {
@@ -159,7 +154,7 @@ class InlineCodegen(
private fun throwCompilationException( private fun throwCompilationException(
nodeAndSmap: SMAPAndMethodNode?, e: Exception, generateNodeText: Boolean nodeAndSmap: SMAPAndMethodNode?, e: Exception, generateNodeText: Boolean
): CompilationException { ): CompilationException {
val contextDescriptor = codegen.getContext().contextDescriptor val contextDescriptor = sourceCompiler.compilationContextDescriptor
val element = DescriptorToSourceUtils.descriptorToDeclaration(contextDescriptor) val element = DescriptorToSourceUtils.descriptorToDeclaration(contextDescriptor)
val node = nodeAndSmap?.node val node = nodeAndSmap?.node
throw CompilationException( throw CompilationException(
@@ -171,7 +166,7 @@ class InlineCodegen(
) )
} }
private fun generateStub(resolvedCall: ResolvedCall<*>?, codegen: ExpressionCodegen) { private fun generateStub(resolvedCall: ResolvedCall<*>?, codegen: BaseExpressionCodegen) {
leaveTemps() leaveTemps()
assert(resolvedCall != null) assert(resolvedCall != null)
val message = "Call is part of inline cycle: " + resolvedCall!!.call.callElement.text val message = "Call is part of inline cycle: " + resolvedCall!!.call.callElement.text
@@ -190,7 +185,7 @@ class InlineCodegen(
private fun inlineCall(nodeAndSmap: SMAPAndMethodNode, callDefault: Boolean): InlineResult { private fun inlineCall(nodeAndSmap: SMAPAndMethodNode, callDefault: Boolean): InlineResult {
assert(delayedHiddenWriting == null) { "'putHiddenParamsIntoLocals' should be called after 'processAndPutHiddenParameters(true)'" } assert(delayedHiddenWriting == null) { "'putHiddenParamsIntoLocals' should be called after 'processAndPutHiddenParameters(true)'" }
val defaultSourceMapper = codegen.parentCodegen.orCreateSourceMapper val defaultSourceMapper = sourceMapper
defaultSourceMapper.callSiteMarker = CallSiteMarker(codegen.lastLineNumber) defaultSourceMapper.callSiteMarker = CallSiteMarker(codegen.lastLineNumber)
val node = nodeAndSmap.node val node = nodeAndSmap.node
if (callDefault) { if (callDefault) {
@@ -216,7 +211,7 @@ class InlineCodegen(
val info = RootInliningContext( val info = RootInliningContext(
expressionMap, state, codegen.inlineNameGenerator.subGenerator(jvmSignature.asmMethod.name), expressionMap, state, codegen.inlineNameGenerator.subGenerator(jvmSignature.asmMethod.name),
callElement, inlineCallSiteInfo, reifiedTypeInliner, typeParameterMappings callElement, sourceCompiler.inlineCallSiteInfo, reifiedTypeInliner, typeParameterMappings
) )
val inliner = MethodInliner( val inliner = MethodInliner(
@@ -235,15 +230,15 @@ class InlineCodegen(
val result = inliner.doInline(adapter, remapper, true, LabelOwner.SKIP_ALL) val result = inliner.doInline(adapter, remapper, true, LabelOwner.SKIP_ALL)
result.reifiedTypeParametersUsages.mergeAll(reificationResult) result.reifiedTypeParametersUsages.mergeAll(reificationResult)
val descriptor = getLabelOwnerDescriptor(codegen.getContext()) val descriptor = sourceCompiler.getLabelOwnerDescriptor()
val labels = getDeclarationLabels(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor) val labels = getDeclarationLabels(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor)
val infos = MethodInliner.processReturns(adapter, LabelOwner { labels.contains(it) }, true, null) val infos = MethodInliner.processReturns(adapter, LabelOwner { labels.contains(it) }, true, null)
generateAndInsertFinallyBlocks( sourceCompiler.generateAndInsertFinallyBlocks(
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 (!isFinallyMarkerRequired(codegen.getContext())) { if (!sourceCompiler.isFinallyMarkerRequired()) {
removeFinallyMarkers(adapter) removeFinallyMarkers(adapter)
} }
@@ -256,57 +251,9 @@ class InlineCodegen(
return result return result
} }
private val inlineCallSiteInfo: InlineCallSiteInfo
get() {
var context = codegen.getContext()
var parentCodegen = codegen.parentCodegen
while (context is InlineLambdaContext) {
val closureContext = context.getParentContext()
assert(closureContext is ClosureContext) { "Parent context of inline lambda should be closure context" }
assert(closureContext.parentContext is MethodContext) { "Closure context should appear in method context" }
context = closureContext.parentContext as MethodContext
assert(parentCodegen is FakeMemberCodegen) { "Parent codegen of inlined lambda should be FakeMemberCodegen" }
parentCodegen = (parentCodegen as FakeMemberCodegen).delegate
}
val signature = typeMapper.mapSignatureSkipGeneric(context.functionDescriptor, context.contextKind)
return InlineCallSiteInfo(
parentCodegen.className, signature.asmMethod.name, signature.asmMethod.descriptor
)
}
private fun generateClosuresBodies() { private fun generateClosuresBodies() {
for (info in expressionMap.values) { for (info in expressionMap.values) {
info.generateLambdaBody(codegen, reifiedTypeInliner) info.generateLambdaBody(sourceCompiler, reifiedTypeInliner)
}
}
private class FakeMemberCodegen(
internal val delegate: MemberCodegen<*>,
declaration: KtElement,
codegenContext: FieldOwnerContext<*>,
private val className: String
) : MemberCodegen<KtPureElement>(delegate as MemberCodegen<KtPureElement>, declaration, codegenContext) {
override fun generateDeclaration() {
throw IllegalStateException()
}
override fun generateBody() {
throw IllegalStateException()
}
override fun generateKotlinMetadataAnnotation() {
throw IllegalStateException()
}
override fun getInlineNameGenerator(): NameGenerator {
return delegate.inlineNameGenerator
}
override //TODO: obtain name from context
fun getClassName(): String {
return className
} }
} }
@@ -376,7 +323,7 @@ class InlineCodegen(
} }
override fun processAndPutHiddenParameters(justProcess: Boolean) { override fun processAndPutHiddenParameters(justProcess: Boolean) {
if (getMethodAsmFlags(functionDescriptor, context.contextKind, state) and Opcodes.ACC_STATIC == 0) { if (getMethodAsmFlags(functionDescriptor, sourceCompiler.contextKind, state) and Opcodes.ACC_STATIC == 0) {
invocationParamBuilder.addNextParameter(AsmTypes.OBJECT_TYPE, false) invocationParamBuilder.addNextParameter(AsmTypes.OBJECT_TYPE, false)
} }
@@ -519,69 +466,6 @@ class InlineCodegen(
putArgumentOrCapturedToLocalVal(stackValue.type, stackValue, paramIndex, paramIndex, ValueKind.CAPTURED) putArgumentOrCapturedToLocalVal(stackValue.type, stackValue, paramIndex, paramIndex, ValueKind.CAPTURED)
} }
private fun generateAndInsertFinallyBlocks(
intoNode: MethodNode,
insertPoints: List<MethodInliner.PointForExternalFinallyBlocks>,
offsetForFinallyLocalVar: Int
) {
if (!codegen.hasFinallyBlocks()) return
val extensionPoints = HashMap<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks>()
for (insertPoint in insertPoints) {
extensionPoints.put(insertPoint.beforeIns, insertPoint)
}
val processor = DefaultProcessor(intoNode, offsetForFinallyLocalVar)
var curFinallyDepth = 0
var curInstr: AbstractInsnNode? = intoNode.instructions.first
while (curInstr != null) {
processor.processInstruction(curInstr, true)
if (isFinallyStart(curInstr)) {
//TODO depth index calc could be more precise
curFinallyDepth = getConstant(curInstr.previous)
}
val extension = extensionPoints[curInstr]
if (extension != null) {
val start = Label()
val finallyNode = createEmptyMethodNode()
finallyNode.visitLabel(start)
val finallyCodegen = ExpressionCodegen(finallyNode, codegen.frameMap, codegen.returnType,
codegen.getContext(), codegen.state, codegen.parentCodegen)
finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.blockStackElements, curFinallyDepth)
val frameMap = finallyCodegen.frameMap
val mark = frameMap.mark()
val marker = processor.localVarsMetaInfo.currentIntervals.maxBy { it.node.index }?.node?.index?.plus(1) ?: -1
while (frameMap.currentSize < Math.max(processor.nextFreeLocalIndex, offsetForFinallyLocalVar + marker)) {
frameMap.enterTemp(Type.INT_TYPE)
}
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
insertNodeBefore(finallyNode, intoNode, curInstr)
val splitBy = SimpleInterval(start.info as LabelNode, extension.finallyIntervalEnd)
processor.tryBlocksMetaInfo.splitCurrentIntervals(splitBy, true)
//processor.getLocalVarsMetaInfo().splitAndRemoveIntervalsFromCurrents(splitBy);
mark.dropTo()
}
curInstr = curInstr.next
}
processor.substituteTryBlockNodes(intoNode)
//processor.substituteLocalVarTable(intoNode);
}
override fun reorderArgumentsIfNeeded( override fun reorderArgumentsIfNeeded(
actualArgsWithDeclIndex: List<ArgumentAndDeclIndex>, valueParameterTypes: List<Type> actualArgsWithDeclIndex: List<ArgumentAndDeclIndex>, valueParameterTypes: List<Type>
) { ) {
@@ -605,13 +489,14 @@ class InlineCodegen(
} }
} }
internal fun createMethodNode( internal fun createInlineMethodNode(
functionDescriptor: FunctionDescriptor, functionDescriptor: FunctionDescriptor,
jvmSignature: JvmMethodSignature, jvmSignature: JvmMethodSignature,
codegen: ExpressionCodegen, codegen: BaseExpressionCodegen,
context: CodegenContext<*>,
callDefault: Boolean, callDefault: Boolean,
resolvedCall: ResolvedCall<*>? resolvedCall: ResolvedCall<*>?,
state: GenerationState,
sourceCompilerForInline: SourceCompilerForInline
): SMAPAndMethodNode { ): SMAPAndMethodNode {
if (isSpecialEnumMethod(functionDescriptor)) { if (isSpecialEnumMethod(functionDescriptor)) {
val arguments = resolvedCall!!.typeArguments val arguments = resolvedCall!!.typeArguments
@@ -620,29 +505,28 @@ class InlineCodegen(
codegen, codegen,
functionDescriptor.name.asString(), functionDescriptor.name.asString(),
arguments.keys.single().defaultType, arguments.keys.single().defaultType,
codegen.state.typeMapper state.typeMapper
) )
return SMAPAndMethodNode(node, SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1)) return SMAPAndMethodNode(node, SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1))
} }
else if (functionDescriptor.isBuiltInSuspendCoroutineOrReturnInJvm()) { else if (functionDescriptor.isBuiltInSuspendCoroutineOrReturnInJvm()) {
return SMAPAndMethodNode( return SMAPAndMethodNode(
createMethodNodeForSuspendCoroutineOrReturn( createMethodNodeForSuspendCoroutineOrReturn(
functionDescriptor, codegen.state.typeMapper functionDescriptor, state.typeMapper
), ),
SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1) SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1)
) )
} }
val state = codegen.state
val asmMethod = if (callDefault) val asmMethod = if (callDefault)
state.typeMapper.mapDefaultMethod(functionDescriptor, context.contextKind) state.typeMapper.mapDefaultMethod(functionDescriptor, sourceCompilerForInline.contextKind)
else else
jvmSignature.asmMethod jvmSignature.asmMethod
val methodId = MethodId(DescriptorUtils.getFqNameSafe(functionDescriptor.containingDeclaration), asmMethod) val methodId = MethodId(DescriptorUtils.getFqNameSafe(functionDescriptor.containingDeclaration), asmMethod)
val directMember = getDirectMemberAndCallableFromObject(functionDescriptor) val directMember = getDirectMemberAndCallableFromObject(functionDescriptor)
if (!isBuiltInArrayIntrinsic(functionDescriptor) && directMember !is DeserializedCallableMemberDescriptor) { if (!isBuiltInArrayIntrinsic(functionDescriptor) && directMember !is DeserializedCallableMemberDescriptor) {
return doCreateMethodNodeFromSource(functionDescriptor, jvmSignature, codegen, context, callDefault, state, asmMethod) return sourceCompilerForInline.doCreateMethodNodeFromSource(functionDescriptor, jvmSignature, callDefault, asmMethod)
} }
val resultInCache = state.inlineCache.methodNodeById.getOrPut(methodId val resultInCache = state.inlineCache.methodNodeById.getOrPut(methodId
@@ -692,58 +576,7 @@ class InlineCodegen(
return getMethodNode(bytes, asmMethod.name, asmMethod.descriptor, containerId.asString()) return getMethodNode(bytes, asmMethod.name, asmMethod.descriptor, containerId.asString())
} }
private fun doCreateMethodNodeFromSource(
callableDescriptor: FunctionDescriptor,
jvmSignature: JvmMethodSignature,
codegen: ExpressionCodegen,
context: CodegenContext<*>,
callDefault: Boolean,
state: GenerationState,
asmMethod: Method
): SMAPAndMethodNode {
val element = DescriptorToSourceUtils.descriptorToDeclaration(callableDescriptor)
if (!(element is KtNamedFunction || element is KtPropertyAccessor)) {
throw IllegalStateException("Couldn't find declaration for function " + callableDescriptor)
}
val inliningFunction = element as KtDeclarationWithBody?
val node = MethodNode(
API,
getMethodAsmFlags(callableDescriptor, context.contextKind, state) or if (callDefault) Opcodes.ACC_STATIC else 0,
asmMethod.name,
asmMethod.descriptor, null, null
)
//for maxLocals calculation
val maxCalcAdapter = wrapWithMaxLocalCalc(node)
val parentContext = context.parentContext ?: error("Context has no parent: " + context)
val methodContext = parentContext.intoFunction(callableDescriptor)
val smap: SMAP
if (callDefault) {
val implementationOwner = state.typeMapper.mapImplementationOwner(callableDescriptor)
val parentCodegen = FakeMemberCodegen(
codegen.parentCodegen, inliningFunction!!, methodContext.parentContext as FieldOwnerContext<*>,
implementationOwner.internalName
)
if (element !is KtNamedFunction) {
throw IllegalStateException("Property accessors with default parameters not supported " + callableDescriptor)
}
FunctionCodegen.generateDefaultImplBody(
methodContext, callableDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
inliningFunction as KtNamedFunction?, parentCodegen, asmMethod
)
smap = createSMAPWithDefaultMapping(inliningFunction, parentCodegen.orCreateSourceMapper.resultMappings)
}
else {
smap = generateMethodBody(maxCalcAdapter, callableDescriptor, methodContext, inliningFunction!!, jvmSignature, codegen, null)
}
maxCalcAdapter.visitMaxs(-1, -1)
maxCalcAdapter.visitEnd()
return SMAPAndMethodNode(node, smap)
}
private fun isBuiltInArrayIntrinsic(callableDescriptor: CallableMemberDescriptor): Boolean { private fun isBuiltInArrayIntrinsic(callableDescriptor: CallableMemberDescriptor): Boolean {
if (callableDescriptor is FictitiousArrayConstructor) return true if (callableDescriptor is FictitiousArrayConstructor) return true
@@ -751,14 +584,6 @@ class InlineCodegen(
return (name == "arrayOf" || name == "emptyArray") && callableDescriptor.containingDeclaration is BuiltInsPackageFragment return (name == "arrayOf" || name == "emptyArray") && callableDescriptor.containingDeclaration is BuiltInsPackageFragment
} }
private fun getLabelOwnerDescriptor(context: MethodContext): CallableMemberDescriptor {
val parentContext = context.parentContext
if (parentContext is ClosureContext && parentContext.originalSuspendLambdaDescriptor != null) {
return parentContext.originalSuspendLambdaDescriptor!!
}
return context.contextDescriptor
}
private fun removeStaticInitializationTrigger(methodNode: MethodNode) { private fun removeStaticInitializationTrigger(methodNode: MethodNode) {
val insnList = methodNode.instructions val insnList = methodNode.instructions
var insn: AbstractInsnNode? = insnList.first var insn: AbstractInsnNode? = insnList.first
@@ -774,79 +599,6 @@ class InlineCodegen(
} }
} }
fun generateMethodBody(
adapter: MethodVisitor,
descriptor: FunctionDescriptor,
context: MethodContext,
expression: KtExpression,
jvmMethodSignature: JvmMethodSignature,
codegen: ExpressionCodegen,
lambdaInfo: ExpressionLambda?
): SMAP {
val isLambda = lambdaInfo != null
val state = codegen.state
// Wrapping for preventing marking actual parent codegen as containing reified markers
val parentCodegen = FakeMemberCodegen(
codegen.parentCodegen, expression, context.parentContext as FieldOwnerContext<*>,
if (isLambda)
codegen.parentCodegen.className
else
state.typeMapper.mapImplementationOwner(descriptor).internalName
)
val strategy: FunctionGenerationStrategy
if (expression is KtCallableReferenceExpression) {
val callableReferenceExpression = expression
val receiverExpression = callableReferenceExpression.receiverExpression
val receiverType = if (receiverExpression != null && codegen.bindingContext.getType(receiverExpression) != null)
codegen.state.typeMapper.mapType(codegen.bindingContext.getType(receiverExpression)!!)
else
null
if (isLambda && lambdaInfo!!.isPropertyReference) {
val asmType = state.typeMapper.mapClass(lambdaInfo.classDescriptor)
val info = lambdaInfo.propertyReferenceInfo
strategy = PropertyReferenceCodegen.PropertyReferenceGenerationStrategy(
true, info!!.getFunction, info.target, asmType, receiverType,
lambdaInfo.functionWithBodyOrCallableReference, state, true)
}
else {
strategy = FunctionReferenceGenerationStrategy(
state,
descriptor,
callableReferenceExpression.callableReference
.getResolvedCallWithAssert(codegen.bindingContext),
receiverType, null,
true
)
}
}
else if (expression is KtFunctionLiteral) {
strategy = ClosureGenerationStrategy(state, expression as KtDeclarationWithBody)
}
else {
strategy = FunctionGenerationStrategy.FunctionDefault(state, expression as KtDeclarationWithBody)
}
FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen)
if (isLambda) {
codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.reifiedTypeParametersUsages)
}
return createSMAPWithDefaultMapping(expression, parentCodegen.orCreateSourceMapper.resultMappings)
}
private fun createSMAPWithDefaultMapping(
declaration: KtExpression,
mappings: List<FileMapping>
): SMAP {
val containingFile = declaration.containingFile
CodegenUtil.getLineNumberForElement(containingFile, true) ?: error("Couldn't extract line count in " + containingFile)
return SMAP(mappings)
}
/*descriptor is null for captured vars*/ /*descriptor is null for captured vars*/
private fun shouldPutGeneralValue(type: Type, stackValue: StackValue): Boolean { private fun shouldPutGeneralValue(type: Type, stackValue: StackValue): Boolean {
@@ -905,38 +657,7 @@ class InlineCodegen(
return result return result
} }
fun getContext(
descriptor: DeclarationDescriptor, state: GenerationState, sourceFile: KtFile?
): CodegenContext<*> {
if (descriptor is PackageFragmentDescriptor) {
return PackageContext(descriptor, state.rootContext, null, sourceFile)
}
val container = descriptor.containingDeclaration ?: error("No container for descriptor: " + descriptor)
val parent = getContext(
container,
state,
sourceFile
)
if (descriptor is ScriptDescriptor) {
val earlierScripts = state.replSpecific.earlierScriptsForReplInterpreter
return parent.intoScript(
descriptor,
earlierScripts ?: emptyList(),
descriptor as ClassDescriptor, state.typeMapper
)
}
else if (descriptor is ClassDescriptor) {
val kind = if (DescriptorUtils.isInterface(descriptor)) OwnerKind.DEFAULT_IMPLS else OwnerKind.IMPLEMENTATION
return parent.intoClass(descriptor, kind, state)
}
else if (descriptor is FunctionDescriptor) {
return parent.intoFunction(descriptor)
}
throw IllegalStateException("Couldn't build context for " + descriptor)
}
fun createNestedSourceMapper(nodeAndSmap: SMAPAndMethodNode, parent: SourceMapper): SourceMapper { fun createNestedSourceMapper(nodeAndSmap: SMAPAndMethodNode, parent: SourceMapper): SourceMapper {
return NestedSourceMapper(parent, nodeAndSmap.sortedRanges, nodeAndSmap.classSMAP.sourceInfo) return NestedSourceMapper(parent, nodeAndSmap.sortedRanges, nodeAndSmap.classSMAP.sourceInfo)
@@ -956,3 +677,6 @@ class InlineCodegen(
} }
} }
} }
val BaseExpressionCodegen.v: InstructionAdapter
get() = visitor
@@ -22,10 +22,9 @@ import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.MethodNode import org.jetbrains.org.objectweb.asm.tree.MethodNode
@@ -33,7 +32,8 @@ import org.jetbrains.org.objectweb.asm.tree.MethodNode
class InlineCodegenForDefaultBody( class InlineCodegenForDefaultBody(
function: FunctionDescriptor, function: FunctionDescriptor,
codegen: ExpressionCodegen, codegen: ExpressionCodegen,
val state: GenerationState val state: GenerationState,
private val sourceCompilerForInline: SourceCompilerForInline
) : CallGenerator() { ) : CallGenerator() {
private val sourceMapper: SourceMapper = codegen.parentCodegen.orCreateSourceMapper private val sourceMapper: SourceMapper = codegen.parentCodegen.orCreateSourceMapper
@@ -45,12 +45,12 @@ class InlineCodegenForDefaultBody(
function.original function.original
private val context = InlineCodegen.getContext( init {
functionDescriptor, state, sourceCompilerForInline.initializeInlineFunctionContext(functionDescriptor)
DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)?.containingFile as? KtFile }
)
private val jvmSignature = state.typeMapper.mapSignatureWithGeneric(functionDescriptor, context.contextKind)
private val jvmSignature: JvmMethodGenericSignature
private val methodStartLabel = Label() private val methodStartLabel = Label()
@@ -58,6 +58,9 @@ class InlineCodegenForDefaultBody(
assert(InlineUtil.isInline(function)) { assert(InlineUtil.isInline(function)) {
"InlineCodegen can inline only inline functions and array constructors: " + function "InlineCodegen can inline only inline functions and array constructors: " + function
} }
sourceCompilerForInline.initializeInlineFunctionContext(functionDescriptor)
jvmSignature = state.typeMapper.mapSignatureWithGeneric(functionDescriptor, sourceCompilerForInline.contextKind)
InlineCodegen.reportIncrementalInfo(functionDescriptor, codegen.context.functionDescriptor.original, jvmSignature, state) InlineCodegen.reportIncrementalInfo(functionDescriptor, codegen.context.functionDescriptor.original, jvmSignature, state)
//InlineCodegenForDefaultBody created just after visitCode call //InlineCodegenForDefaultBody created just after visitCode call
@@ -65,7 +68,7 @@ class InlineCodegenForDefaultBody(
} }
override fun genCallInner(callableMethod: Callable, resolvedCall: ResolvedCall<*>?, callDefault: Boolean, codegen: ExpressionCodegen) { override fun genCallInner(callableMethod: Callable, resolvedCall: ResolvedCall<*>?, callDefault: Boolean, codegen: ExpressionCodegen) {
val nodeAndSmap = InlineCodegen.createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault, null) val nodeAndSmap = InlineCodegen.createInlineMethodNode(functionDescriptor, jvmSignature, codegen, callDefault, null, state, sourceCompilerForInline)
val childSourceMapper = InlineCodegen.createNestedSourceMapper(nodeAndSmap, sourceMapper) val childSourceMapper = InlineCodegen.createNestedSourceMapper(nodeAndSmap, sourceMapper)
val node = nodeAndSmap.node val node = nodeAndSmap.node
@@ -84,7 +87,7 @@ class InlineCodegenForDefaultBody(
} }
}) })
transformedMethod.accept(MethodBodyVisitor(codegen.v)) transformedMethod.accept(MethodBodyVisitor(codegen.visitor))
} }
override fun genValueAndPut(valueParameterDescriptor: ValueParameterDescriptor, argumentExpression: KtExpression, parameterType: Type, parameterIndex: Int) { override fun genValueAndPut(valueParameterDescriptor: ValueParameterDescriptor, argumentExpression: KtExpression, parameterType: Type, parameterIndex: Int) {
@@ -55,7 +55,7 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : LabelOwner {
lateinit var node: SMAPAndMethodNode lateinit var node: SMAPAndMethodNode
abstract fun generateLambdaBody(codegen: ExpressionCodegen, reifiedTypeInliner: ReifiedTypeInliner) abstract fun generateLambdaBody(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner)
fun addAllParameters(remapper: FieldRemapper): Parameters { fun addAllParameters(remapper: FieldRemapper): Parameters {
val builder = ParametersBuilder.initializeBuilderFrom(AsmTypes.OBJECT_TYPE, invokeMethod.descriptor, this) val builder = ParametersBuilder.initializeBuilderFrom(AsmTypes.OBJECT_TYPE, invokeMethod.descriptor, this)
@@ -105,8 +105,8 @@ 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(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner) {
val classReader = buildClassReaderByInternalName(codegen.state, lambdaClassType.internalName) val classReader = buildClassReaderByInternalName(sourceCompiler.state, lambdaClassType.internalName)
var isPropertyReference = false var isPropertyReference = false
var isFunctionReference = false var isFunctionReference = false
classReader.accept(object: ClassVisitor(API){ classReader.accept(object: ClassVisitor(API){
@@ -153,7 +153,7 @@ class DefaultLambda(
invokeMethod = Method( invokeMethod = Method(
(if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE).asString(), (if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE).asString(),
codegen.state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor).asmMethod.descriptor sourceCompiler.state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor).asmMethod.descriptor
) )
node = getMethodNode( node = getMethodNode(
@@ -260,24 +260,17 @@ class ExpressionLambda(
val isPropertyReference: Boolean val isPropertyReference: Boolean
get() = propertyReferenceInfo != null get() = propertyReferenceInfo != null
override fun generateLambdaBody(codegen: ExpressionCodegen, reifiedTypeInliner: ReifiedTypeInliner) { override fun generateLambdaBody(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner) {
val closureContext =
if (isPropertyReference)
codegen.getContext().intoAnonymousClass(classDescriptor, codegen, OwnerKind.IMPLEMENTATION)
else
codegen.getContext().intoClosure(invokeMethodDescriptor, codegen, typeMapper)
val context = closureContext.intoInlinedLambda(invokeMethodDescriptor, isCrossInline, isPropertyReference)
val jvmMethodSignature = typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor) val jvmMethodSignature = typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor)
val asmMethod = jvmMethodSignature.asmMethod val asmMethod = jvmMethodSignature.asmMethod
val methodNode = MethodNode( val methodNode = MethodNode(
API, AsmUtil.getMethodAsmFlags(invokeMethodDescriptor, context.contextKind, codegen.state), API, AsmUtil.getMethodAsmFlags(invokeMethodDescriptor, OwnerKind.IMPLEMENTATION, sourceCompiler.state),
asmMethod.name, asmMethod.descriptor, null, null asmMethod.name, asmMethod.descriptor, null, null
) )
node = wrapWithMaxLocalCalc(methodNode).let { adapter -> node = wrapWithMaxLocalCalc(methodNode).let { adapter ->
val smap = InlineCodegen.generateMethodBody( val smap = sourceCompiler.generateLambdaBody(
adapter, invokeMethodDescriptor, context, functionWithBodyOrCallableReference, jvmMethodSignature, codegen, this adapter, jvmMethodSignature, this
) )
adapter.visitMaxs(-1, -1) adapter.visitMaxs(-1, -1)
SMAPAndMethodNode(methodNode, smap) SMAPAndMethodNode(methodNode, smap)
@@ -0,0 +1,365 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.context.*
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.LabelNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import java.util.HashMap
import kotlin.properties.Delegates
class SourceCompilerForInline(private val codegen: ExpressionCodegen) {
val state = codegen.state
private var context by Delegates.notNull<CodegenContext<*>>()
val contextKind
get () = context.contextKind
val inlineCallSiteInfo: InlineCallSiteInfo
get() {
var context = codegen.getContext()
var parentCodegen = codegen.parentCodegen
while (context is InlineLambdaContext) {
val closureContext = context.getParentContext()
assert(closureContext is ClosureContext) { "Parent context of inline lambda should be closure context" }
assert(closureContext.parentContext is MethodContext) { "Closure context should appear in method context" }
context = closureContext.parentContext as MethodContext
assert(parentCodegen is FakeMemberCodegen) { "Parent codegen of inlined lambda should be FakeMemberCodegen" }
parentCodegen = (parentCodegen as FakeMemberCodegen).delegate
}
val signature = codegen.state.typeMapper.mapSignatureSkipGeneric(context.functionDescriptor, context.contextKind)
return InlineCallSiteInfo(
parentCodegen.className, signature.asmMethod.name, signature.asmMethod.descriptor
)
}
val lazySourceMapper
get() = codegen.parentCodegen.orCreateSourceMapper
fun generateLambdaBody(adapter: MethodVisitor,
jvmMethodSignature: JvmMethodSignature,
lambdaInfo: ExpressionLambda): SMAP {
val invokeMethodDescriptor = lambdaInfo.invokeMethodDescriptor
val closureContext =
if (lambdaInfo.isPropertyReference)
codegen.getContext().intoAnonymousClass(lambdaInfo.classDescriptor, codegen, OwnerKind.IMPLEMENTATION)
else
codegen.getContext().intoClosure(invokeMethodDescriptor, codegen, state.typeMapper)
val context = closureContext.intoInlinedLambda(invokeMethodDescriptor, lambdaInfo.isCrossInline, lambdaInfo.isPropertyReference)
return generateMethodBody(
adapter, invokeMethodDescriptor, context,
lambdaInfo.functionWithBodyOrCallableReference,
jvmMethodSignature, lambdaInfo
)
}
private fun generateMethodBody(
adapter: MethodVisitor,
descriptor: FunctionDescriptor,
context: MethodContext,
expression: KtExpression,
jvmMethodSignature: JvmMethodSignature,
lambdaInfo: ExpressionLambda?
): SMAP {
val isLambda = lambdaInfo != null
// Wrapping for preventing marking actual parent codegen as containing reified markers
val parentCodegen = FakeMemberCodegen(
codegen.parentCodegen, expression, context.parentContext as FieldOwnerContext<*>,
if (isLambda)
codegen.parentCodegen.className
else
state.typeMapper.mapImplementationOwner(descriptor).internalName
)
val strategy = when (expression) {
is KtCallableReferenceExpression -> {
val callableReferenceExpression = expression
val receiverExpression = callableReferenceExpression.receiverExpression
val receiverType = if (receiverExpression != null && state.bindingContext.getType(receiverExpression) != null)
state.typeMapper.mapType(state.bindingContext.getType(receiverExpression)!!)
else
null
if (isLambda && lambdaInfo!!.isPropertyReference) {
val asmType = state.typeMapper.mapClass(lambdaInfo.classDescriptor)
val info = lambdaInfo.propertyReferenceInfo
PropertyReferenceCodegen.PropertyReferenceGenerationStrategy(
true, info!!.getFunction, info.target, asmType, receiverType,
lambdaInfo.functionWithBodyOrCallableReference, state, true)
}
else {
FunctionReferenceGenerationStrategy(
state,
descriptor,
callableReferenceExpression.callableReference
.getResolvedCallWithAssert(state.bindingContext),
receiverType, null,
true
)
}
}
is KtFunctionLiteral -> ClosureGenerationStrategy(state, expression as KtDeclarationWithBody)
else -> FunctionGenerationStrategy.FunctionDefault(state, expression as KtDeclarationWithBody)
}
FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen)
if (isLambda) {
codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.reifiedTypeParametersUsages)
}
return createSMAPWithDefaultMapping(expression, parentCodegen.orCreateSourceMapper.resultMappings)
}
private fun createSMAPWithDefaultMapping(
declaration: KtExpression,
mappings: List<FileMapping>
): SMAP {
val containingFile = declaration.containingFile
CodegenUtil.getLineNumberForElement(containingFile, true) ?: error("Couldn't extract line count in " + containingFile)
return SMAP(mappings)
}
@Suppress("UNCHECKED_CAST")
private class FakeMemberCodegen(
internal val delegate: MemberCodegen<*>,
declaration: KtElement,
codegenContext: FieldOwnerContext<*>,
private val className: String
) : MemberCodegen<KtPureElement>(delegate as MemberCodegen<KtPureElement>, declaration, codegenContext) {
override fun generateDeclaration() {
throw IllegalStateException()
}
override fun generateBody() {
throw IllegalStateException()
}
override fun generateKotlinMetadataAnnotation() {
throw IllegalStateException()
}
override fun getInlineNameGenerator(): NameGenerator {
return delegate.inlineNameGenerator
}
override //TODO: obtain name from context
fun getClassName(): String {
return className
}
}
fun doCreateMethodNodeFromSource(
callableDescriptor: FunctionDescriptor,
jvmSignature: JvmMethodSignature,
callDefault: Boolean,
asmMethod: Method
): SMAPAndMethodNode {
val element = DescriptorToSourceUtils.descriptorToDeclaration(callableDescriptor)
if (!(element is KtNamedFunction || element is KtPropertyAccessor)) {
throw IllegalStateException("Couldn't find declaration for function " + callableDescriptor)
}
val inliningFunction = element as KtDeclarationWithBody?
val node = MethodNode(
API,
AsmUtil.getMethodAsmFlags(callableDescriptor, context.contextKind, state) or if (callDefault) Opcodes.ACC_STATIC else 0,
asmMethod.name,
asmMethod.descriptor, null, null
)
//for maxLocals calculation
val maxCalcAdapter = wrapWithMaxLocalCalc(node)
val parentContext = context.parentContext ?: error("Context has no parent: " + context)
val methodContext = parentContext.intoFunction(callableDescriptor)
val smap: SMAP
if (callDefault) {
val implementationOwner = state.typeMapper.mapImplementationOwner(callableDescriptor)
val parentCodegen = FakeMemberCodegen(
codegen.parentCodegen, inliningFunction!!, methodContext.parentContext as FieldOwnerContext<*>,
implementationOwner.internalName
)
if (element !is KtNamedFunction) {
throw IllegalStateException("Property accessors with default parameters not supported " + callableDescriptor)
}
FunctionCodegen.generateDefaultImplBody(
methodContext, callableDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
inliningFunction as KtNamedFunction?, parentCodegen, asmMethod
)
smap = createSMAPWithDefaultMapping(inliningFunction, parentCodegen.orCreateSourceMapper.resultMappings)
}
else {
smap = generateMethodBody(maxCalcAdapter, callableDescriptor, methodContext, inliningFunction!!, jvmSignature, null)
}
maxCalcAdapter.visitMaxs(-1, -1)
maxCalcAdapter.visitEnd()
return SMAPAndMethodNode(node, smap)
}
fun generateAndInsertFinallyBlocks(
intoNode: MethodNode,
insertPoints: List<MethodInliner.PointForExternalFinallyBlocks>,
offsetForFinallyLocalVar: Int
) {
if (!codegen.hasFinallyBlocks()) return
val extensionPoints = HashMap<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks>()
for (insertPoint in insertPoints) {
extensionPoints.put(insertPoint.beforeIns, insertPoint)
}
val processor = DefaultProcessor(intoNode, offsetForFinallyLocalVar)
var curFinallyDepth = 0
var curInstr: AbstractInsnNode? = intoNode.instructions.first
while (curInstr != null) {
processor.processInstruction(curInstr, true)
if (isFinallyStart(curInstr)) {
//TODO depth index calc could be more precise
curFinallyDepth = getConstant(curInstr.previous)
}
val extension = extensionPoints[curInstr]
if (extension != null) {
val start = Label()
val finallyNode = createEmptyMethodNode()
finallyNode.visitLabel(start)
val finallyCodegen = ExpressionCodegen(finallyNode, codegen.frameMap, codegen.returnType,
codegen.getContext(), codegen.state, codegen.parentCodegen)
finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.blockStackElements, curFinallyDepth)
val frameMap = finallyCodegen.frameMap
val mark = frameMap.mark()
var marker = -1
val intervals = processor.localVarsMetaInfo.currentIntervals
for (interval in intervals) {
marker = Math.max(interval.node.index + 1, marker)
}
while (frameMap.currentSize < Math.max(processor.nextFreeLocalIndex, offsetForFinallyLocalVar + marker)) {
frameMap.enterTemp(Type.INT_TYPE)
}
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
insertNodeBefore(finallyNode, intoNode, curInstr)
val splitBy = SimpleInterval(start.info as LabelNode, extension.finallyIntervalEnd)
processor.tryBlocksMetaInfo.splitCurrentIntervals(splitBy, true)
//processor.getLocalVarsMetaInfo().splitAndRemoveIntervalsFromCurrents(splitBy);
mark.dropTo()
}
curInstr = curInstr.next
}
processor.substituteTryBlockNodes(intoNode)
//processor.substituteLocalVarTable(intoNode);
}
fun isCallInsideSameModuleAsDeclared(functionDescriptor: FunctionDescriptor) : Boolean {
return JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), codegen.state.outDirectory)
}
fun isFinallyMarkerRequired(): Boolean = isFinallyMarkerRequired(codegen.getContext())
val compilationContextDescriptor
get() = codegen.getContext().contextDescriptor
val compilationContextFunctionDescriptor
get() = codegen.getContext().functionDescriptor
fun getLabelOwnerDescriptor(): CallableMemberDescriptor {
val context = codegen.getContext()
val parentContext = context.parentContext
if (parentContext is ClosureContext && parentContext.originalSuspendLambdaDescriptor != null) {
return parentContext.originalSuspendLambdaDescriptor!!
}
return context.contextDescriptor
}
fun initializeInlineFunctionContext(functionDescriptor: FunctionDescriptor) {
context = getContext(functionDescriptor, state, DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)?.containingFile as? KtFile)
}
companion object {
fun getContext(
descriptor: DeclarationDescriptor, state: GenerationState, sourceFile: KtFile?
): CodegenContext<*> {
if (descriptor is PackageFragmentDescriptor) {
return PackageContext(descriptor, state.rootContext, null, sourceFile)
}
val container = descriptor.containingDeclaration ?: error("No container for descriptor: " + descriptor)
val parent = getContext(
container,
state,
sourceFile
)
if (descriptor is ScriptDescriptor) {
val earlierScripts = state.replSpecific.earlierScriptsForReplInterpreter
return parent.intoScript(
descriptor,
earlierScripts ?: emptyList(),
descriptor as ClassDescriptor, state.typeMapper
)
}
else if (descriptor is ClassDescriptor) {
val kind = if (DescriptorUtils.isInterface(descriptor)) OwnerKind.DEFAULT_IMPLS else OwnerKind.IMPLEMENTATION
return parent.intoClass(descriptor, kind, state)
}
else if (descriptor is FunctionDescriptor) {
return parent.intoFunction(descriptor)
}
throw IllegalStateException("Couldn't build context for " + descriptor)
}
}
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.inline
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.BaseExpressionCodegen
import org.jetbrains.kotlin.codegen.ExpressionCodegen import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.codegen.JvmCodegenUtil
import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.binding.CodegenBinding
@@ -502,7 +503,7 @@ internal fun isSpecialEnumMethod(functionDescriptor: FunctionDescriptor): Boolea
} }
internal fun createSpecialEnumMethodBody( internal fun createSpecialEnumMethodBody(
codegen: ExpressionCodegen, codegen: BaseExpressionCodegen,
name: String, name: String,
type: KotlinType, type: KotlinType,
typeMapper: KotlinTypeMapper typeMapper: KotlinTypeMapper
@@ -511,7 +512,7 @@ internal fun createSpecialEnumMethodBody(
val invokeType = typeMapper.mapType(type) val invokeType = typeMapper.mapType(type)
val desc = getSpecialEnumFunDescriptor(invokeType, isValueOf) val desc = getSpecialEnumFunDescriptor(invokeType, isValueOf)
val node = 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, InstructionAdapter(node)) ExpressionCodegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.ENUM_REIFIED, InstructionAdapter(node), codegen)
if (isValueOf) { if (isValueOf) {
node.visitInsn(Opcodes.ACONST_NULL) node.visitInsn(Opcodes.ACONST_NULL)
node.visitVarInsn(Opcodes.ALOAD, 0) node.visitVarInsn(Opcodes.ALOAD, 0)
@@ -569,7 +570,7 @@ fun FunctionDescriptor.getClassFilePath(typeMapper: KotlinTypeMapper, cache: Inc
} }
} }
class InlineOnlySmapSkipper(codegen: ExpressionCodegen) { class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) {
private val callLineNumber = codegen.lastLineNumber private val callLineNumber = codegen.lastLineNumber