Unify code generation of as/is operation
They should be the same for common and reified cases
This commit is contained in:
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.codegen.inline.*;
|
|||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethod;
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethod;
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicPropertyGetter;
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicPropertyGetter;
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics;
|
|
||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt;
|
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt;
|
||||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
|
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
@@ -3674,30 +3673,17 @@ The "returned" value of try expression with no finally is either the last expres
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean safeAs = opToken == KtTokens.AS_SAFE;
|
boolean safeAs = opToken == KtTokens.AS_SAFE;
|
||||||
|
Type type = boxType(asmType(rightType));
|
||||||
if (TypeUtils.isReifiedTypeParameter(rightType)) {
|
if (TypeUtils.isReifiedTypeParameter(rightType)) {
|
||||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(rightType,
|
putReifiedOperationMarkerIfTypeIsReifiedParameter(rightType,
|
||||||
safeAs ? ReifiedTypeInliner.OperationKind.SAFE_AS
|
safeAs ? ReifiedTypeInliner.OperationKind.SAFE_AS
|
||||||
: ReifiedTypeInliner.OperationKind.AS);
|
: ReifiedTypeInliner.OperationKind.AS);
|
||||||
v.checkcast(boxType(asmType(rightType)));
|
v.checkcast(type);
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opToken != KtTokens.AS_SAFE) {
|
CodegenUtilKt.generateAsCast(v, rightType, type, safeAs);
|
||||||
if (!TypeUtils.isNullableType(rightType)) {
|
|
||||||
CodegenUtilKt.generateNullCheckForNonSafeAs(v, rightType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
v.dup();
|
|
||||||
generateInstanceOfInstruction(rightType);
|
|
||||||
Label ok = new Label();
|
|
||||||
v.ifne(ok);
|
|
||||||
v.pop();
|
|
||||||
v.aconst(null);
|
|
||||||
v.mark(ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
generateCheckCastInstruction(rightType, safeAs);
|
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -3749,38 +3735,19 @@ The "returned" value of try expression with no finally is either the last expres
|
|||||||
v.dup();
|
v.dup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type type = boxType(asmType(kotlinType));
|
||||||
if (TypeUtils.isReifiedTypeParameter(kotlinType)) {
|
if (TypeUtils.isReifiedTypeParameter(kotlinType)) {
|
||||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(kotlinType, ReifiedTypeInliner.OperationKind.IS);
|
putReifiedOperationMarkerIfTypeIsReifiedParameter(kotlinType, ReifiedTypeInliner.OperationKind.IS);
|
||||||
v.instanceOf(boxType(asmType(kotlinType)));
|
v.instanceOf(type);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
CodegenUtilKt.generateIsCheck(v, kotlinType.isMarkedNullable(), new Function1<InstructionAdapter, Unit>() {
|
CodegenUtilKt.generateIsCheck(v, kotlinType, type);
|
||||||
@Override
|
|
||||||
public Unit invoke(InstructionAdapter adapter) {
|
|
||||||
generateInstanceOfInstruction(kotlinType);
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateInstanceOfInstruction(@NotNull KotlinType kotlinType) {
|
|
||||||
Type type = boxType(asmType(kotlinType));
|
|
||||||
assert !TypeUtils.isReifiedTypeParameter(kotlinType) : "This method should not be called with type based on reified type parameter, but found " + kotlinType;
|
|
||||||
TypeIntrinsics.instanceOf(v, kotlinType, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private StackValue generateCheckCastInstruction(@NotNull KotlinType kotlinType, boolean safeAs) {
|
|
||||||
Type type = boxType(asmType(kotlinType));
|
|
||||||
assert !TypeUtils.isReifiedTypeParameter(kotlinType) : "This method should not be called with type based on reified type parameter, but found " + kotlinType;
|
|
||||||
TypeIntrinsics.checkcast(v, kotlinType, type, safeAs);
|
|
||||||
return StackValue.onStack(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void putReifiedOperationMarkerIfTypeIsReifiedParameter(
|
public void putReifiedOperationMarkerIfTypeIsReifiedParameter(
|
||||||
@NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind
|
@NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -886,13 +886,7 @@ public class FunctionCodegen {
|
|||||||
iv.ifnonnull(afterBarrier);
|
iv.ifnonnull(afterBarrier);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
CodegenUtilKt.generateIsCheck(iv, kotlinType.isMarkedNullable(), new Function1<InstructionAdapter, Unit>() {
|
CodegenUtilKt.generateIsCheck(iv, kotlinType, boxType(delegateParameterType));
|
||||||
@Override
|
|
||||||
public Unit invoke(InstructionAdapter adapter) {
|
|
||||||
TypeIntrinsics.instanceOf(adapter, kotlinType, boxType(delegateParameterType));
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
iv.ifne(afterBarrier);
|
iv.ifne(afterBarrier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
package org.jetbrains.kotlin.codegen
|
package org.jetbrains.kotlin.codegen
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
|
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
|
||||||
|
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo
|
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo
|
||||||
@@ -28,15 +29,17 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
|||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.types.ErrorUtils
|
import org.jetbrains.kotlin.types.ErrorUtils
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
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.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
|
|
||||||
fun generateIsCheck(
|
fun generateIsCheck(
|
||||||
v: InstructionAdapter,
|
v: InstructionAdapter,
|
||||||
isNullable: Boolean,
|
kotlinType: KotlinType,
|
||||||
generateInstanceOfInstruction: (InstructionAdapter) -> Unit
|
asmType: Type
|
||||||
) {
|
) {
|
||||||
if (isNullable) {
|
if (TypeUtils.isNullableType(kotlinType)) {
|
||||||
val nope = Label()
|
val nope = Label()
|
||||||
val end = Label()
|
val end = Label()
|
||||||
|
|
||||||
@@ -45,7 +48,8 @@ fun generateIsCheck(
|
|||||||
|
|
||||||
ifnull(nope)
|
ifnull(nope)
|
||||||
|
|
||||||
generateInstanceOfInstruction(this)
|
TypeIntrinsics.instanceOf(this, kotlinType, asmType)
|
||||||
|
|
||||||
goTo(end)
|
goTo(end)
|
||||||
|
|
||||||
mark(nope)
|
mark(nope)
|
||||||
@@ -56,11 +60,37 @@ fun generateIsCheck(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
generateInstanceOfInstruction(v)
|
TypeIntrinsics.instanceOf(v, kotlinType, asmType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateNullCheckForNonSafeAs(
|
fun generateAsCast(
|
||||||
|
v: InstructionAdapter,
|
||||||
|
kotlinType: KotlinType,
|
||||||
|
asmType: Type,
|
||||||
|
isSafe: Boolean
|
||||||
|
) {
|
||||||
|
if (!isSafe) {
|
||||||
|
if (!TypeUtils.isNullableType(kotlinType)) {
|
||||||
|
generateNullCheckForNonSafeAs(v, kotlinType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
with(v) {
|
||||||
|
dup()
|
||||||
|
TypeIntrinsics.instanceOf(v, kotlinType, asmType)
|
||||||
|
val ok = Label()
|
||||||
|
ifne(ok)
|
||||||
|
pop()
|
||||||
|
aconst(null)
|
||||||
|
mark(ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeIntrinsics.checkcast(v, kotlinType, asmType, isSafe)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateNullCheckForNonSafeAs(
|
||||||
v: InstructionAdapter,
|
v: InstructionAdapter,
|
||||||
type: KotlinType
|
type: KotlinType
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -17,10 +17,9 @@
|
|||||||
package org.jetbrains.kotlin.codegen.inline
|
package org.jetbrains.kotlin.codegen.inline
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.context.MethodContext
|
import org.jetbrains.kotlin.codegen.context.MethodContext
|
||||||
|
import org.jetbrains.kotlin.codegen.generateAsCast
|
||||||
import org.jetbrains.kotlin.codegen.generateIsCheck
|
import org.jetbrains.kotlin.codegen.generateIsCheck
|
||||||
import org.jetbrains.kotlin.codegen.generateNullCheckForNonSafeAs
|
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
|
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
@@ -177,64 +176,42 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
|
|||||||
|
|
||||||
private fun processAs(insn: MethodInsnNode,
|
private fun processAs(insn: MethodInsnNode,
|
||||||
instructions: InsnList,
|
instructions: InsnList,
|
||||||
jetType: KotlinType,
|
kotlinType: KotlinType,
|
||||||
asmType: Type,
|
asmType: Type,
|
||||||
safe: Boolean) =
|
safe: Boolean) =
|
||||||
rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { instanceofInsn: AbstractInsnNode ->
|
rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { stubCheckcast: AbstractInsnNode ->
|
||||||
if (instanceofInsn !is TypeInsnNode) return false
|
if (stubCheckcast !is TypeInsnNode) return false
|
||||||
|
|
||||||
|
val newMethodNode = MethodNode(InlineCodegenUtil.API)
|
||||||
|
generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe)
|
||||||
|
|
||||||
|
instructions.insert(insn, newMethodNode.instructions)
|
||||||
|
instructions.remove(stubCheckcast)
|
||||||
|
|
||||||
|
// TODO: refine max stack calculation (it's not always as big as +4)
|
||||||
|
maxStackSize = Math.max(maxStackSize, 4)
|
||||||
|
|
||||||
addNullCheckForAsIfNeeded(insn.previous.previous, instructions, jetType, safe)
|
|
||||||
TypeIntrinsics.checkcast(instanceofInsn, instructions, jetType, asmType, safe)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addNullCheckForAsIfNeeded(insn: AbstractInsnNode, instructions: InsnList, jetType: KotlinType, safe: Boolean) {
|
private fun processIs(insn: MethodInsnNode,
|
||||||
if (!safe && !TypeUtils.isNullableType(jetType)) {
|
instructions: InsnList,
|
||||||
val methodNode = MethodNode(InlineCodegenUtil.API)
|
kotlinType: KotlinType,
|
||||||
generateNullCheckForNonSafeAs(InstructionAdapter(methodNode), jetType)
|
asmType: Type) =
|
||||||
|
rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { stubInstanceOf: AbstractInsnNode ->
|
||||||
|
if (stubInstanceOf !is TypeInsnNode) return false
|
||||||
|
|
||||||
InlineCodegenUtil.insertNodeBefore(methodNode, instructions, insn)
|
val newMethodNode = MethodNode(InlineCodegenUtil.API)
|
||||||
maxStackSize = Math.max(maxStackSize, 4)
|
generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processIs(insn: MethodInsnNode, instructions: InsnList, jetType: KotlinType, asmType: Type) =
|
instructions.insert(insn, newMethodNode.instructions)
|
||||||
rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { instanceofInsn: AbstractInsnNode ->
|
instructions.remove(stubInstanceOf)
|
||||||
if (instanceofInsn !is TypeInsnNode) return false
|
|
||||||
|
|
||||||
addNullCheckForIsIfNeeded(insn, instructions, jetType)
|
// TODO: refine max stack calculation (it's not always as big as +2)
|
||||||
TypeIntrinsics.instanceOf(instanceofInsn, instructions, jetType, asmType)
|
maxStackSize = Math.max(maxStackSize, 2)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addNullCheckForIsIfNeeded(insn: AbstractInsnNode, instructions: InsnList, type: KotlinType) {
|
|
||||||
if (TypeUtils.isNullableType(type)) {
|
|
||||||
val instanceOf = insn.next
|
|
||||||
insertNullCheckAround(instructions, insn.previous.previous, instanceOf)
|
|
||||||
maxStackSize = Math.max(maxStackSize, 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun insertNullCheckAround(instructions: InsnList, start: AbstractInsnNode, end: AbstractInsnNode) {
|
|
||||||
val methodNode = MethodNode(InlineCodegenUtil.API)
|
|
||||||
var splitIndex: Int = -1
|
|
||||||
generateIsCheck(InstructionAdapter(methodNode), true) {
|
|
||||||
splitIndex = methodNode.instructions.size()
|
|
||||||
}
|
|
||||||
assert(splitIndex >= 0) {
|
|
||||||
"Split index should be non-negative, but $splitIndex"
|
|
||||||
}
|
|
||||||
|
|
||||||
val nullCheckInsns = methodNode.instructions.toArray()
|
|
||||||
nullCheckInsns.take(splitIndex).forEach {
|
|
||||||
instructions.insertBefore(start, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
nullCheckInsns.drop(splitIndex).reversed().forEach {
|
|
||||||
instructions.insert(end, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline private fun rewriteNextTypeInsn(
|
inline private fun rewriteNextTypeInsn(
|
||||||
marker: MethodInsnNode,
|
marker: MethodInsnNode,
|
||||||
expectedNextOpcode: Int,
|
expectedNextOpcode: Int,
|
||||||
|
|||||||
@@ -108,40 +108,6 @@ public object TypeIntrinsics {
|
|||||||
v.checkcast(asmType)
|
v.checkcast(asmType)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
public fun checkcast(
|
|
||||||
checkcastInsn: TypeInsnNode,
|
|
||||||
instructions: InsnList,
|
|
||||||
kotlinType: KotlinType, asmType: Type,
|
|
||||||
safe: Boolean) {
|
|
||||||
if (safe) {
|
|
||||||
checkcastInsn.desc = asmType.internalName
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val functionTypeArity = getFunctionTypeArity(kotlinType)
|
|
||||||
if (functionTypeArity >= 0) {
|
|
||||||
instructions.insertBefore(checkcastInsn, iconstNode(functionTypeArity))
|
|
||||||
|
|
||||||
val beforeCheckcast = typeIntrinsicNode(BEFORE_CHECKCAST_TO_FUNCTION_OF_ARITY, BEFORE_CHECKCAST_TO_FUNCTION_OF_ARITY_DESCRIPTOR)
|
|
||||||
instructions.insertBefore(checkcastInsn, beforeCheckcast)
|
|
||||||
|
|
||||||
instructions.insertBefore(checkcastInsn, TypeInsnNode(Opcodes.CHECKCAST, asmType.internalName))
|
|
||||||
instructions.remove(checkcastInsn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val asMutableCollectionMethodName = getAsMutableCollectionMethodName(kotlinType)
|
|
||||||
if (asMutableCollectionMethodName != null) {
|
|
||||||
instructions.insertBefore(checkcastInsn,
|
|
||||||
typeIntrinsicNode(asMutableCollectionMethodName, getAsMutableCollectionDescriptor(asmType)))
|
|
||||||
instructions.remove(checkcastInsn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
checkcastInsn.desc = asmType.internalName
|
|
||||||
}
|
|
||||||
|
|
||||||
private val INTRINSICS_CLASS = "kotlin/jvm/internal/TypeIntrinsics"
|
private val INTRINSICS_CLASS = "kotlin/jvm/internal/TypeIntrinsics"
|
||||||
|
|
||||||
private val IS_FUNCTON_OF_ARITY_METHOD_NAME = "isFunctionOfArity"
|
private val IS_FUNCTON_OF_ARITY_METHOD_NAME = "isFunctionOfArity"
|
||||||
|
|||||||
Reference in New Issue
Block a user