Turn SwitchCodegenUtil into a class

This commit is contained in:
Dmitry Petrov
2017-08-04 17:58:46 +03:00
parent 435cfeea0a
commit 656f8bb5cf
4 changed files with 44 additions and 68 deletions
@@ -50,7 +50,7 @@ import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter;
import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.codegen.when.SwitchCodegen; import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil; import org.jetbrains.kotlin.codegen.when.SwitchCodegenProvider;
import org.jetbrains.kotlin.config.ApiVersion; import org.jetbrains.kotlin.config.ApiVersion;
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor; import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
@@ -131,6 +131,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
private final MemberCodegen<?> parentCodegen; private final MemberCodegen<?> parentCodegen;
private final TailRecursionCodegen tailRecursionCodegen; private final TailRecursionCodegen tailRecursionCodegen;
public final CallGenerator defaultCallGenerator = new CallGenerator.DefaultCallGenerator(this); public final CallGenerator defaultCallGenerator = new CallGenerator.DefaultCallGenerator(this);
private final SwitchCodegenProvider switchCodegenProvider;
private final Stack<BlockStackElement> blockStackElements = new Stack<>(); private final Stack<BlockStackElement> blockStackElements = new Stack<>();
@@ -162,6 +163,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
this.parentCodegen = parentCodegen; this.parentCodegen = parentCodegen;
this.tailRecursionCodegen = new TailRecursionCodegen(context, this, this.v, state); this.tailRecursionCodegen = new TailRecursionCodegen(context, this, this.v, state);
this.switchCodegenProvider = new SwitchCodegenProvider(this);
} }
@Nullable @Nullable
@@ -4122,12 +4124,12 @@ The "returned" value of try expression with no finally is either the last expres
Type resultType = isStatement ? Type.VOID_TYPE : expressionType(expression); Type resultType = isStatement ? Type.VOID_TYPE : expressionType(expression);
return StackValue.operation(resultType, v -> { return StackValue.operation(resultType, v -> {
SwitchCodegen switchCodegen = SwitchCodegenUtil.buildAppropriateSwitchCodegenIfPossible( SwitchCodegen switchCodegen = switchCodegenProvider.buildAppropriateSwitchCodegenIfPossible(
expression, isStatement, CodegenUtil.isExhaustive(bindingContext, expression, isStatement), this expression, isStatement, CodegenUtil.isExhaustive(bindingContext, expression, isStatement)
); );
if (switchCodegen != null) { if (switchCodegen != null) {
switchCodegen.generate(); switchCodegen.generate();
return Unit.INSTANCE; return null;
} }
int subjectLocal = expr != null ? myFrameMap.enterTemp(subjectType) : -1; int subjectLocal = expr != null ? myFrameMap.enterTemp(subjectType) : -1;
@@ -30,7 +30,7 @@ import org.jetbrains.kotlin.codegen.*;
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt; import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil; import org.jetbrains.kotlin.codegen.when.SwitchCodegenProvider;
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping; import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt; import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.*;
@@ -83,7 +83,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
private final JvmRuntimeTypes runtimeTypes; private final JvmRuntimeTypes runtimeTypes;
private final JvmFileClassesProvider fileClassesProvider; private final JvmFileClassesProvider fileClassesProvider;
private final TypeMappingConfiguration<Type> typeMappingConfiguration; private final TypeMappingConfiguration<Type> typeMappingConfiguration;
private final boolean shouldInlineConstVals; private final SwitchCodegenProvider switchCodegenProvider;
public CodegenAnnotatingVisitor(@NotNull GenerationState state) { public CodegenAnnotatingVisitor(@NotNull GenerationState state) {
this.bindingTrace = state.getBindingTrace(); this.bindingTrace = state.getBindingTrace();
@@ -92,7 +92,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
this.runtimeTypes = state.getJvmRuntimeTypes(); this.runtimeTypes = state.getJvmRuntimeTypes();
this.fileClassesProvider = state.getFileClassesProvider(); this.fileClassesProvider = state.getFileClassesProvider();
this.typeMappingConfiguration = state.getTypeMapper().getTypeMappingConfiguration(); this.typeMappingConfiguration = state.getTypeMapper().getTypeMappingConfiguration();
this.shouldInlineConstVals = state.getShouldInlineConstVals(); this.switchCodegenProvider = new SwitchCodegenProvider(state);
} }
@NotNull @NotNull
@@ -682,7 +682,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
WhenByEnumsMapping mapping = new WhenByEnumsMapping(classDescriptor, currentClassName, fieldNumber); WhenByEnumsMapping mapping = new WhenByEnumsMapping(classDescriptor, currentClassName, fieldNumber);
for (ConstantValue<?> constant : SwitchCodegenUtil.getAllConstants(expression, bindingContext, shouldInlineConstVals)) { for (ConstantValue<?> constant : switchCodegenProvider.getAllConstants(expression)) {
if (constant instanceof NullValue) continue; if (constant instanceof NullValue) continue;
assert constant instanceof EnumValue : "expression in when should be EnumValue"; assert constant instanceof EnumValue : "expression in when should be EnumValue";
@@ -696,10 +696,8 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
private boolean isWhenWithEnums(@NotNull KtWhenExpression expression) { private boolean isWhenWithEnums(@NotNull KtWhenExpression expression) {
return WhenChecker.isWhenByEnum(expression, bindingContext) && return WhenChecker.isWhenByEnum(expression, bindingContext) &&
SwitchCodegenUtil.checkAllItemsAreConstantsSatisfying( switchCodegenProvider.checkAllItemsAreConstantsSatisfying(
expression, expression,
bindingContext,
shouldInlineConstVals,
constant -> constant instanceof EnumValue || constant instanceof NullValue constant -> constant instanceof EnumValue || constant instanceof NullValue
); );
} }
@@ -49,6 +49,8 @@ abstract public class SwitchCodegen {
protected Label endLabel = new Label(); protected Label endLabel = new Label();
protected Label defaultLabel; protected Label defaultLabel;
protected final SwitchCodegenProvider switchCodegenProvider;
public SwitchCodegen( public SwitchCodegen(
@NotNull KtWhenExpression expression, boolean isStatement, @NotNull KtWhenExpression expression, boolean isStatement,
boolean isExhaustive, @NotNull ExpressionCodegen codegen, boolean isExhaustive, @NotNull ExpressionCodegen codegen,
@@ -59,6 +61,7 @@ abstract public class SwitchCodegen {
this.isExhaustive = isExhaustive; this.isExhaustive = isExhaustive;
this.codegen = codegen; this.codegen = codegen;
this.bindingContext = codegen.getBindingContext(); this.bindingContext = codegen.getBindingContext();
this.switchCodegenProvider = new SwitchCodegenProvider(codegen);
this.subjectType = subjectType != null ? subjectType : codegen.expressionType(expression.getSubjectExpression()); this.subjectType = subjectType != null ? subjectType : codegen.expressionType(expression.getSubjectExpression());
resultType = isStatement ? Type.VOID_TYPE : codegen.expressionType(expression); resultType = isStatement ? Type.VOID_TYPE : codegen.expressionType(expression);
@@ -100,7 +103,7 @@ abstract public class SwitchCodegen {
for (KtWhenEntry entry : expression.getEntries()) { for (KtWhenEntry entry : expression.getEntries()) {
Label entryLabel = new Label(); Label entryLabel = new Label();
for (ConstantValue<?> constant : SwitchCodegenUtil.getConstantsFromEntry(entry, bindingContext, codegen.getState().getShouldInlineConstVals())) { for (ConstantValue<?> constant : switchCodegenProvider.getConstantsFromEntry(entry)) {
if (constant instanceof NullValue) continue; if (constant instanceof NullValue) continue;
processConstant(constant, entryLabel); processConstant(constant, entryLabel);
} }
@@ -158,7 +161,7 @@ abstract public class SwitchCodegen {
private int findNullEntryIndex(@NotNull KtWhenExpression expression) { private int findNullEntryIndex(@NotNull KtWhenExpression expression) {
int entryIndex = 0; int entryIndex = 0;
for (KtWhenEntry entry : expression.getEntries()) { for (KtWhenEntry entry : expression.getEntries()) {
for (ConstantValue<?> constant : SwitchCodegenUtil.getConstantsFromEntry(entry, bindingContext, codegen.getState().getShouldInlineConstVals())) { for (ConstantValue<?> constant : switchCodegenProvider.getConstantsFromEntry(entry)) {
if (constant instanceof NullValue) { if (constant instanceof NullValue) {
return entryIndex; return entryIndex;
} }
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.`when`
import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.ExpressionCodegen import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.constants.ConstantValue import org.jetbrains.kotlin.resolve.constants.ConstantValue
@@ -29,14 +30,16 @@ import org.jetbrains.org.objectweb.asm.Type
import java.util.ArrayList import java.util.ArrayList
object SwitchCodegenUtil { class SwitchCodegenProvider
@JvmStatic private constructor (
fun checkAllItemsAreConstantsSatisfying( private val bindingContext: BindingContext,
expression: KtWhenExpression, private val shouldInlineConstVals: Boolean,
bindingContext: BindingContext, private val codegen: ExpressionCodegen?
shouldInlineConstVals: Boolean, ) {
predicate: Function1<ConstantValue<*>, Boolean> constructor(state: GenerationState) : this(state.bindingContext, state.shouldInlineConstVals, null)
): Boolean = constructor(codegen: ExpressionCodegen) : this(codegen.bindingContext, codegen.state.shouldInlineConstVals, codegen)
fun checkAllItemsAreConstantsSatisfying(expression: KtWhenExpression, predicate: Function1<ConstantValue<*>, Boolean>): Boolean =
expression.entries.all { entry -> expression.entries.all { entry ->
entry.conditions.all { condition -> entry.conditions.all { condition ->
if (condition !is KtWhenConditionWithExpression) return false if (condition !is KtWhenConditionWithExpression) return false
@@ -46,33 +49,19 @@ object SwitchCodegenUtil {
} }
} }
@JvmStatic fun getAllConstants(expression: KtWhenExpression): Iterable<ConstantValue<*>?> =
fun getAllConstants(
expression: KtWhenExpression,
bindingContext: BindingContext,
shouldInlineConstVals: Boolean
): Iterable<ConstantValue<*>?> =
ArrayList<ConstantValue<*>?>().apply { ArrayList<ConstantValue<*>?>().apply {
for (entry in expression.entries) { for (entry in expression.entries) {
addConstantsFromConditions(entry, bindingContext, shouldInlineConstVals) addConstantsFromConditions(entry)
} }
} }
@JvmStatic fun getConstantsFromEntry(entry: KtWhenEntry): Iterable<ConstantValue<*>?> =
fun getConstantsFromEntry(
entry: KtWhenEntry,
bindingContext: BindingContext,
shouldInlineConstVals: Boolean
): Iterable<ConstantValue<*>?> =
ArrayList<ConstantValue<*>?>().apply { ArrayList<ConstantValue<*>?>().apply {
addConstantsFromConditions(entry, bindingContext, shouldInlineConstVals) addConstantsFromConditions(entry)
} }
private fun ArrayList<ConstantValue<*>?>.addConstantsFromConditions( private fun ArrayList<ConstantValue<*>?>.addConstantsFromConditions(entry: KtWhenEntry) {
entry: KtWhenEntry,
bindingContext: BindingContext,
shouldInlineConstVals: Boolean
) {
for (condition in entry.conditions) { for (condition in entry.conditions) {
if (condition !is KtWhenConditionWithExpression) continue if (condition !is KtWhenConditionWithExpression) continue
val patternExpression = condition.expression ?: throw AssertionError("expression in when should not be null") val patternExpression = condition.expression ?: throw AssertionError("expression in when should not be null")
@@ -80,16 +69,14 @@ object SwitchCodegenUtil {
} }
} }
@JvmStatic
fun buildAppropriateSwitchCodegenIfPossible( fun buildAppropriateSwitchCodegenIfPossible(
expression: KtWhenExpression, expression: KtWhenExpression,
isStatement: Boolean, isStatement: Boolean,
isExhaustive: Boolean, isExhaustive: Boolean
codegen: ExpressionCodegen
): SwitchCodegen? { ): SwitchCodegen? {
val bindingContext = codegen.bindingContext val codegen = codegen ?: throw AssertionError("Can't create SwitchCodegen in this context")
val shouldInlineConstVals = codegen.state.shouldInlineConstVals
if (!isThereConstantEntriesButNulls(expression, bindingContext, shouldInlineConstVals)) { if (!isThereConstantEntriesButNulls(expression)) {
return null return null
} }
@@ -100,37 +87,23 @@ object SwitchCodegenUtil {
return when { return when {
mapping != null -> mapping != null ->
EnumSwitchCodegen(expression, isStatement, isExhaustive, codegen, mapping) EnumSwitchCodegen(expression, isStatement, isExhaustive, codegen, mapping)
isIntegralConstantsSwitch(expression, subjectType, bindingContext, shouldInlineConstVals) -> isIntegralConstantsSwitch(expression, subjectType) ->
IntegralConstantsSwitchCodegen(expression, isStatement, isExhaustive, codegen) IntegralConstantsSwitchCodegen(expression, isStatement, isExhaustive, codegen)
isStringConstantsSwitch(expression, subjectType, bindingContext, shouldInlineConstVals) -> isStringConstantsSwitch(expression, subjectType) ->
StringSwitchCodegen(expression, isStatement, isExhaustive, codegen) StringSwitchCodegen(expression, isStatement, isExhaustive, codegen)
else -> null else -> null
} }
} }
private fun isThereConstantEntriesButNulls( private fun isThereConstantEntriesButNulls(expression: KtWhenExpression): Boolean =
expression: KtWhenExpression, getAllConstants(expression).any { it != null && it !is NullValue }
bindingContext: BindingContext,
shouldInlineConstVals: Boolean
): Boolean =
getAllConstants(expression, bindingContext, shouldInlineConstVals).any { it != null && it !is NullValue }
private fun isIntegralConstantsSwitch( private fun isIntegralConstantsSwitch(expression: KtWhenExpression, subjectType: Type): Boolean =
expression: KtWhenExpression,
subjectType: Type,
bindingContext: BindingContext,
shouldInlineConstVals: Boolean
): Boolean =
AsmUtil.isIntPrimitive(subjectType) && AsmUtil.isIntPrimitive(subjectType) &&
checkAllItemsAreConstantsSatisfying(expression, bindingContext, shouldInlineConstVals) { it is IntegerValueConstant<*> } checkAllItemsAreConstantsSatisfying(expression) { it is IntegerValueConstant<*> }
private fun isStringConstantsSwitch( private fun isStringConstantsSwitch(expression: KtWhenExpression, subjectType: Type): Boolean =
expression: KtWhenExpression,
subjectType: Type,
bindingContext: BindingContext,
shouldInlineConstVals: Boolean
): Boolean =
subjectType.className == String::class.java.name && subjectType.className == String::class.java.name &&
checkAllItemsAreConstantsSatisfying(expression, bindingContext, shouldInlineConstVals) { it is StringValue || it is NullValue } checkAllItemsAreConstantsSatisfying(expression) { it is StringValue || it is NullValue }
} }