From 5a1c995b5c08990fc497e1a117a2448a5d6b099d Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 15 Jul 2014 19:55:39 +0400 Subject: [PATCH] When by enum: Generate TABLESWITCH/LOOKUPSWITCH bytecode command in case of "when" by enum entries --- .../org/jetbrains/jet/codegen/AsmUtil.java | 5 + .../binding/CodegenAnnotatingVisitor.java | 82 +++++++++- .../jet/codegen/binding/CodegenBinding.java | 7 + .../jet/codegen/state/GenerationState.java | 9 ++ .../jet/codegen/when/EnumSwitchCodegen.java | 53 +++++++ .../MappingClassesForWhenByEnumCodegen.java | 147 ++++++++++++++++++ .../when/MappingsClassesForWhenByEnum.java | 58 +++++++ .../jet/codegen/when/SwitchCodegenUtil.java | 13 ++ .../jet/codegen/when/WhenByEnumsMapping.java | 77 +++++++++ .../jetbrains/jet/lang/cfg/WhenChecker.java | 25 ++- .../whenEnumOptimization/bigEnum.kt | 49 ++++++ .../whenEnumOptimization/duplicatingItems.kt | 25 +++ .../enumInsideClassObject.kt | 29 ++++ .../whenEnumOptimization/expression.kt | 37 +++++ .../functionLiteralInTopLevel.kt | 19 +++ .../manyWhensWithinClass.kt | 50 ++++++ .../whenEnumOptimization/nonConstantEnum.kt | 16 ++ .../whenEnumOptimization/subjectAny.kt | 26 ++++ .../whenEnumOptimization/withoutElse.kt | 41 +++++ .../whenEnumOptimization/bigEnum.kt | 26 ++++ .../whenEnumOptimization/duplicatingItems.kt | 18 +++ .../whenEnumOptimization/expression.kt | 27 ++++ .../functionLiteralInTopLevel.kt | 22 +++ .../manyWhensWithinClass.kt | 36 +++++ .../whenEnumOptimization/nonConstantEnum.kt | 18 +++ .../whenEnumOptimization/subjectAny.kt | 18 +++ .../whenEnumOptimization/withoutElse.kt | 31 ++++ .../codegen/BytecodeTextTestGenerated.java | 51 +++++- ...lackBoxWithStdlibCodegenTestGenerated.java | 56 ++++++- 29 files changed, 1059 insertions(+), 12 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/when/EnumSwitchCodegen.java create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/when/MappingClassesForWhenByEnumCodegen.java create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/when/MappingsClassesForWhenByEnum.java create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/when/WhenByEnumsMapping.java create mode 100644 compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/bigEnum.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/duplicatingItems.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/enumInsideClassObject.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/expression.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/functionLiteralInTopLevel.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/manyWhensWithinClass.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/nonConstantEnum.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/subjectAny.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/withoutElse.kt create mode 100644 compiler/testData/codegen/bytecodeText/whenEnumOptimization/bigEnum.kt create mode 100644 compiler/testData/codegen/bytecodeText/whenEnumOptimization/duplicatingItems.kt create mode 100644 compiler/testData/codegen/bytecodeText/whenEnumOptimization/expression.kt create mode 100644 compiler/testData/codegen/bytecodeText/whenEnumOptimization/functionLiteralInTopLevel.kt create mode 100644 compiler/testData/codegen/bytecodeText/whenEnumOptimization/manyWhensWithinClass.kt create mode 100644 compiler/testData/codegen/bytecodeText/whenEnumOptimization/nonConstantEnum.kt create mode 100644 compiler/testData/codegen/bytecodeText/whenEnumOptimization/subjectAny.kt create mode 100644 compiler/testData/codegen/bytecodeText/whenEnumOptimization/withoutElse.kt diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java index 9b469c9a249..09a3a516694 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java @@ -788,4 +788,9 @@ public class AsmUtil { v.aconst(type); } } + + @NotNull + public static Type getArrayOf(@NotNull String internalClassName) { + return Type.getType("[L" + internalClassName + ";"); + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenAnnotatingVisitor.java b/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenAnnotatingVisitor.java index a67dd79adaf..608ce19731b 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenAnnotatingVisitor.java @@ -20,13 +20,14 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.util.containers.Stack; +import kotlin.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.codegen.AsmUtil; -import org.jetbrains.jet.codegen.JvmRuntimeTypes; -import org.jetbrains.jet.codegen.SamCodegenUtil; -import org.jetbrains.jet.codegen.SamType; +import org.jetbrains.jet.codegen.*; import org.jetbrains.jet.codegen.state.GenerationState; +import org.jetbrains.jet.codegen.when.SwitchCodegenUtil; +import org.jetbrains.jet.codegen.when.WhenByEnumsMapping; +import org.jetbrains.jet.lang.cfg.WhenChecker; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorImpl; import org.jetbrains.jet.lang.psi.*; @@ -37,8 +38,11 @@ import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPacka import org.jetbrains.jet.lang.resolve.calls.model.ExpressionValueArgument; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument; +import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; +import org.jetbrains.jet.lang.resolve.constants.EnumValue; import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; +import org.jetbrains.jet.lang.resolve.kotlin.PackagePartClassUtils; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.JetScope; @@ -489,4 +493,74 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { } } } + + @Override + public void visitWhenExpression(@NotNull JetWhenExpression expression) { + super.visitWhenExpression(expression); + if (isWhenWithEnums(expression)) { + String currentClassName = getCurrentTopLevelClassOrPackagePartInternalName(expression.getContainingJetFile()); + + if (bindingContext.get(MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE, currentClassName) == null) { + bindingTrace.record( + MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE, + currentClassName, + new ArrayList() + ); + } + + List mappings = bindingContext.get(MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE, currentClassName); + assert mappings != null : "guaranteed by contract"; + + int fieldNumber = mappings.size(); + + JetType type = bindingContext.get(BindingContext.EXPRESSION_TYPE, expression.getSubjectExpression()); + assert type != null : "should not be null in a valid when by enums"; + ClassDescriptor classDescriptor = (ClassDescriptor) type.getConstructor().getDeclarationDescriptor(); + assert classDescriptor != null : "because it's enum"; + + WhenByEnumsMapping mapping = new WhenByEnumsMapping( + CodegenBinding.getAsmType(bindingContext, classDescriptor).getInternalName(), + currentClassName, + fieldNumber + ); + + for (CompileTimeConstant constant : SwitchCodegenUtil.getAllConstants(expression, bindingContext)) { + assert constant instanceof EnumValue : "expression in when should be EnumValue"; + mapping.putFirstTime((EnumValue) constant, mapping.size() + 1); + } + + mappings.add(mapping); + + bindingTrace.record(MAPPING_FOR_WHEN_BY_ENUM, expression, mapping); + } + } + + private boolean isWhenWithEnums(@NotNull JetWhenExpression expression) { + return WhenChecker.isWhenByEnum(expression, bindingContext) && + SwitchCodegenUtil.checkAllItemsAreConstantsSatisfying( + expression, + bindingContext, + new Function1() { + @Override + public Boolean invoke( + @NotNull CompileTimeConstant constant + ) { + return constant instanceof EnumValue; + } + } + ); + } + + @NotNull + private String getCurrentTopLevelClassOrPackagePartInternalName(@NotNull JetFile file) { + ListIterator iterator = classStack.listIterator(classStack.size()); + while(iterator.hasPrevious()) { + ClassDescriptor previous = iterator.previous().getDescriptor(); + if (DescriptorUtils.isTopLevelOrInnerClass(previous)) { + return CodegenBinding.getAsmType(bindingContext, previous).getInternalName(); + } + } + + return PackagePartClassUtils.getPackagePartInternalName(file); + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenBinding.java b/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenBinding.java index d8d2f46ce66..8e4e542a566 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenBinding.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/binding/CodegenBinding.java @@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.codegen.SamType; import org.jetbrains.jet.codegen.state.GenerationState; +import org.jetbrains.jet.codegen.when.WhenByEnumsMapping; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorImpl; import org.jetbrains.jet.lang.psi.*; @@ -60,6 +61,12 @@ public class CodegenBinding { public static final WritableSlice SAM_VALUE = Slices.createSimpleSlice(); + public static final WritableSlice MAPPING_FOR_WHEN_BY_ENUM = Slices. + sliceBuilder().build(); + + public static final WritableSlice> MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE = + Slices.>sliceBuilder().build(); + static { BasicWritableSlice.initSliceDebugNames(CodegenBinding.class); } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/state/GenerationState.java b/compiler/backend/src/org/jetbrains/jet/codegen/state/GenerationState.java index 81cd73084ad..c94123338bc 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/state/GenerationState.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/state/GenerationState.java @@ -25,6 +25,7 @@ import org.jetbrains.jet.codegen.inline.InlineCodegenUtil; import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethods; import org.jetbrains.jet.codegen.optimization.OptimizationClassBuilderFactory; import org.jetbrains.jet.codegen.optimization.OptimizationUtils; +import org.jetbrains.jet.codegen.when.MappingsClassesForWhenByEnum; import org.jetbrains.jet.lang.descriptors.ModuleDescriptor; import org.jetbrains.jet.lang.descriptors.ScriptDescriptor; import org.jetbrains.jet.lang.diagnostics.DiagnosticHolder; @@ -79,6 +80,9 @@ public class GenerationState { @NotNull private final SamWrapperClasses samWrapperClasses = new SamWrapperClasses(this); + @NotNull + private final MappingsClassesForWhenByEnum mappingsClassesForWhenByEnum = new MappingsClassesForWhenByEnum(this); + @NotNull private final BindingTrace bindingTrace; @@ -222,6 +226,11 @@ public class GenerationState { return samWrapperClasses; } + @NotNull + public MappingsClassesForWhenByEnum getMappingsClassesForWhenByEnum() { + return mappingsClassesForWhenByEnum; + } + public boolean isGenerateNotNullAssertions() { return generateNotNullAssertions; } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/when/EnumSwitchCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/when/EnumSwitchCodegen.java new file mode 100644 index 00000000000..082f397ed42 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/when/EnumSwitchCodegen.java @@ -0,0 +1,53 @@ +package org.jetbrains.jet.codegen.when; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.codegen.ExpressionCodegen; +import org.jetbrains.jet.lang.psi.JetWhenExpression; +import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; +import org.jetbrains.jet.lang.resolve.constants.EnumValue; +import org.jetbrains.org.objectweb.asm.Label; +import org.jetbrains.org.objectweb.asm.Type; + +public class EnumSwitchCodegen extends SwitchCodegen { + private final WhenByEnumsMapping mapping; + + public EnumSwitchCodegen( + @NotNull JetWhenExpression expression, + boolean isStatement, + @NotNull ExpressionCodegen codegen, + @NotNull WhenByEnumsMapping mapping + ) { + super(expression, isStatement, codegen); + this.mapping = mapping; + } + + @Override + protected void generateSubject() { + codegen.getState().getMappingsClassesForWhenByEnum().generateMappingsClassForExpression(expression); + + v.getstatic( + mapping.getMappingsClassInternalName(), + mapping.getFieldName(), + MappingClassesForWhenByEnumCodegen.MAPPINGS_FIELD_DESCRIPTOR + ); + + super.generateSubject(); + + v.invokevirtual( + mapping.getEnumClassInternalName(), + "ordinal", + Type.getMethodDescriptor(Type.INT_TYPE), + false + ); + v.aload(Type.INT_TYPE); + } + + @Override + protected void processConstant( + @NotNull CompileTimeConstant constant, + @NotNull Label entryLabel + ) { + assert constant instanceof EnumValue : "guaranteed by usage contract"; + putTransitionOnce(mapping.getIndexByEntry((EnumValue) constant), entryLabel); + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/when/MappingClassesForWhenByEnumCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/when/MappingClassesForWhenByEnumCodegen.java new file mode 100644 index 00000000000..c30f4c7f2fe --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/when/MappingClassesForWhenByEnumCodegen.java @@ -0,0 +1,147 @@ +/* + * Copyright 2010-2014 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.jet.codegen.when; + +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.codegen.AsmUtil; +import org.jetbrains.jet.codegen.ClassBuilder; +import org.jetbrains.jet.codegen.state.GenerationState; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.constants.EnumValue; +import org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin; +import org.jetbrains.org.objectweb.asm.MethodVisitor; +import org.jetbrains.org.objectweb.asm.Type; +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; + +import java.util.List; +import java.util.Map; + +import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE; +import static org.jetbrains.org.objectweb.asm.Opcodes.*; + +public class MappingClassesForWhenByEnumCodegen { + public static final String MAPPINGS_FIELD_DESCRIPTOR = Type.getDescriptor(int[].class); + private final GenerationState state; + + public MappingClassesForWhenByEnumCodegen(@NotNull GenerationState state) { + this.state = state; + } + + public void generate( + @NotNull List mappings, + @NotNull Type mappingsClass, + @NotNull JetFile srcFile + ) { + ClassBuilder cb = state.getFactory().newVisitor( + JvmDeclarationOrigin.NO_ORIGIN, + mappingsClass, + srcFile + ); + cb.defineClass( + srcFile, + V1_6, + ACC_FINAL | ACC_SYNTHETIC, + mappingsClass.getInternalName(), + null, + OBJECT_TYPE.getInternalName(), + ArrayUtil.EMPTY_STRING_ARRAY + ); + + generateFields(cb, mappings); + generateInitialization(cb, mappings); + + cb.done(); + } + + private static void generateFields( + @NotNull ClassBuilder cb, + @NotNull List mappings + ) { + for (WhenByEnumsMapping mapping : mappings) { + cb.newField( + JvmDeclarationOrigin.NO_ORIGIN, + ACC_STATIC | ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC, + mapping.getFieldName(), + MAPPINGS_FIELD_DESCRIPTOR, + null, null + ); + } + } + + private static void generateInitialization( + @NotNull ClassBuilder cb, + @NotNull List mappings + ) { + MethodVisitor mv = cb.newMethod( + JvmDeclarationOrigin.NO_ORIGIN, + ACC_STATIC | ACC_SYNTHETIC, "", "()V", null, ArrayUtil.EMPTY_STRING_ARRAY + ); + + mv.visitCode(); + + InstructionAdapter v = new InstructionAdapter(mv); + + for (WhenByEnumsMapping mapping : mappings) { + generateInitializationForMapping(cb, v, mapping); + } + + v.areturn(Type.VOID_TYPE); + + mv.visitMaxs(-1, -1); + mv.visitEnd(); + } + + private static void generateInitializationForMapping( + @NotNull ClassBuilder cb, + @NotNull InstructionAdapter v, + @NotNull WhenByEnumsMapping mapping + ) { + v.invokestatic( + mapping.getEnumClassInternalName(), "values", + Type.getMethodDescriptor( + AsmUtil.getArrayOf(mapping.getEnumClassInternalName()) + ), + false + ); + v.arraylength(); + + v.newarray(Type.INT_TYPE); + v.putstatic(cb.getThisName(), mapping.getFieldName(), MAPPINGS_FIELD_DESCRIPTOR); + + String enumClassDesc = Type.getObjectType(mapping.getEnumClassInternalName()).getDescriptor(); + + for (Map.Entry item : mapping.enumValuesToIntMapping()) { + EnumValue enumEntry = item.getKey(); + int mappedValue = item.getValue(); + + v.getstatic(cb.getThisName(), mapping.getFieldName(), MAPPINGS_FIELD_DESCRIPTOR); + v.getstatic( + mapping.getEnumClassInternalName(), + enumEntry.getValue().getName().asString(), + enumClassDesc + ); + v.invokevirtual( + mapping.getEnumClassInternalName(), "ordinal", + Type.getMethodDescriptor(Type.INT_TYPE), + false + ); + v.iconst(mappedValue); + v.astore(Type.INT_TYPE); + } + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/when/MappingsClassesForWhenByEnum.java b/compiler/backend/src/org/jetbrains/jet/codegen/when/MappingsClassesForWhenByEnum.java new file mode 100644 index 00000000000..c9b167d357e --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/when/MappingsClassesForWhenByEnum.java @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2014 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.jet.codegen.when; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.codegen.binding.CodegenBinding; +import org.jetbrains.jet.codegen.state.GenerationState; +import org.jetbrains.jet.lang.psi.JetWhenExpression; +import org.jetbrains.org.objectweb.asm.Type; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class MappingsClassesForWhenByEnum { + private final GenerationState state; + private final Set generatedMappingClasses = new HashSet(); + private final MappingClassesForWhenByEnumCodegen mappingsCodegen; + + public MappingsClassesForWhenByEnum(@NotNull GenerationState state) { + this.state = state; + this.mappingsCodegen = new MappingClassesForWhenByEnumCodegen(state); + } + + public void generateMappingsClassForExpression(@NotNull JetWhenExpression expression) { + WhenByEnumsMapping mapping = state.getBindingContext().get(CodegenBinding.MAPPING_FOR_WHEN_BY_ENUM, expression); + + assert mapping != null : "mapping class should not be requested for non enum when"; + + if (!generatedMappingClasses.contains(mapping.getMappingsClassInternalName())) { + List mappings = state.getBindingContext().get( + CodegenBinding.MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE, + mapping.getOuterClassInternalNameForExpression() + ); + + assert mappings != null : "guaranteed by usage contract of EnumSwitchCodegen"; + + Type mappingsClassType = Type.getObjectType(mapping.getMappingsClassInternalName()); + + mappingsCodegen.generate(mappings, mappingsClassType, expression.getContainingJetFile()); + generatedMappingClasses.add(mapping.getMappingsClassInternalName()); + } + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/when/SwitchCodegenUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/when/SwitchCodegenUtil.java index a9985396891..c8ed6a94cec 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/when/SwitchCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/when/SwitchCodegenUtil.java @@ -20,6 +20,7 @@ import kotlin.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.codegen.ExpressionCodegen; +import org.jetbrains.jet.codegen.binding.CodegenBinding; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; @@ -35,6 +36,12 @@ public class SwitchCodegenUtil { @NotNull Type subjectType, @NotNull BindingContext bindingContext ) { + // *switch opcode can be used if each item is enum entry or integral constant + // in case of enum CodegenAnnotationVisitor should put mappings into bindingContext + if (bindingContext.get(CodegenBinding.MAPPING_FOR_WHEN_BY_ENUM, expression) != null) { + return true; + } + int typeSort = subjectType.getSort(); if (typeSort != Type.INT && typeSort != Type.CHAR && typeSort != Type.SHORT && typeSort != Type.BYTE) { @@ -120,6 +127,12 @@ public class SwitchCodegenUtil { boolean isStatement, @NotNull ExpressionCodegen codegen ) { + WhenByEnumsMapping mapping = codegen.getBindingContext().get(CodegenBinding.MAPPING_FOR_WHEN_BY_ENUM, expression); + + if (mapping != null) { + return new EnumSwitchCodegen(expression, isStatement, codegen, mapping); + } + return new IntegralConstantsSwitchCodegen(expression, isStatement, codegen); } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/when/WhenByEnumsMapping.java b/compiler/backend/src/org/jetbrains/jet/codegen/when/WhenByEnumsMapping.java new file mode 100644 index 00000000000..60b29ba0664 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/when/WhenByEnumsMapping.java @@ -0,0 +1,77 @@ +/* + * Copyright 2010-2014 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.jet.codegen.when; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.resolve.constants.EnumValue; + +import java.util.HashMap; +import java.util.Map; + +public class WhenByEnumsMapping { + private static final String MAPPING_ARRAY_FIELD_PREFIX = "$EnumSwitchMapping$"; + private static final String MAPPINGS_CLASS_NAME_POSTFIX = "$WhenMappings"; + + private final Map map = new HashMap(); + private final String enumClassInternalName; + private final String outerClassInternalNameForExpression; + private final String mappingsClassInternalName; + private final int fieldNumber; + + public WhenByEnumsMapping(String enumClassInternalName, String outerClassInternalNameForExpression, int fieldNumber) { + this.enumClassInternalName = enumClassInternalName; + this.outerClassInternalNameForExpression = outerClassInternalNameForExpression; + this.mappingsClassInternalName = outerClassInternalNameForExpression + MAPPINGS_CLASS_NAME_POSTFIX; + this.fieldNumber = fieldNumber; + } + + public int getIndexByEntry(@NotNull EnumValue value) { + Integer result = map.get(value); + assert result != null : "entry " + value + " has no mapping"; + return result; + } + + public void putFirstTime(EnumValue value, int index) { + if (!map.containsKey(value)) { + map.put(value, index); + } + } + + public int size() { + return map.size(); + } + + public String getFieldName() { + return MAPPING_ARRAY_FIELD_PREFIX + fieldNumber; + } + + public String getEnumClassInternalName() { + return enumClassInternalName; + } + + public String getOuterClassInternalNameForExpression() { + return outerClassInternalNameForExpression; + } + + public String getMappingsClassInternalName() { + return mappingsClassInternalName; + } + + public Iterable> enumValuesToIntMapping() { + return map.entrySet(); + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/WhenChecker.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/WhenChecker.java index 91423f348f8..ed8e349793f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/WhenChecker.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/WhenChecker.java @@ -42,15 +42,28 @@ public final class WhenChecker { return !isUnit && !isStatement && !isWhenExhaustive(expression, trace); } - private static boolean isWhenExhaustive(@NotNull JetWhenExpression expression, @NotNull BindingTrace trace) { + public static boolean isWhenByEnum(@NotNull JetWhenExpression expression, @NotNull BindingContext context) { + return getSubjectClassDescriptorIfEnum(expression, context) != null; + } + + private static ClassDescriptor getSubjectClassDescriptorIfEnum(@NotNull JetWhenExpression expression, @NotNull BindingContext context) { JetExpression subjectExpression = expression.getSubjectExpression(); - if (subjectExpression == null) return false; - JetType type = trace.get(BindingContext.EXPRESSION_TYPE, subjectExpression); - if (type == null) return false; + if (subjectExpression == null) return null; + JetType type = context.get(BindingContext.EXPRESSION_TYPE, subjectExpression); + if (type == null) return null; DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor(); - if (!(declarationDescriptor instanceof ClassDescriptor)) return false; + if (!(declarationDescriptor instanceof ClassDescriptor)) return null; ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; - if (classDescriptor.getKind() != ClassKind.ENUM_CLASS || classDescriptor.getModality().isOverridable()) return false; + if (classDescriptor.getKind() != ClassKind.ENUM_CLASS || classDescriptor.getModality().isOverridable()) return null; + + return classDescriptor; + } + + private static boolean isWhenExhaustive(@NotNull JetWhenExpression expression, @NotNull BindingTrace trace) { + ClassDescriptor classDescriptor = getSubjectClassDescriptorIfEnum(expression, trace.getBindingContext()); + + if (classDescriptor == null) return false; + boolean isExhaust = true; boolean notEmpty = false; for (DeclarationDescriptor descriptor : classDescriptor.getUnsubstitutedInnerClassesScope().getAllDescriptors()) { diff --git a/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/bigEnum.kt b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/bigEnum.kt new file mode 100644 index 00000000000..dd36953d00b --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/bigEnum.kt @@ -0,0 +1,49 @@ +import kotlin.test.assertEquals + +enum class BigEnum { + ITEM1 ITEM2 ITEM3 ITEM4 ITEM5 ITEM6 ITEM7 ITEM8 ITEM9 ITEM10 ITEM11 ITEM12 ITEM13 ITEM14 ITEM15 ITEM16 ITEM17 ITEM18 ITEM19 ITEM20 +} + +fun bar1(x : BigEnum) : String { + when (x) { + BigEnum.ITEM1, BigEnum.ITEM2, BigEnum.ITEM3 -> return "123" + BigEnum.ITEM4, BigEnum.ITEM5, BigEnum.ITEM6 -> return "456" + } + + return "-1"; + +} + +fun bar2(x : BigEnum) : String { + when (x) { + BigEnum.ITEM7, BigEnum.ITEM8, BigEnum.ITEM9 -> return "789" + BigEnum.ITEM10 -> return "10" + BigEnum.ITEM11, BigEnum.ITEM12 -> return "1112" + else -> return "-1" + } +} + +fun box() : String { + //bar1 + assertEquals("123", bar1(BigEnum.ITEM1)) + assertEquals("123", bar1(BigEnum.ITEM2)) + assertEquals("123", bar1(BigEnum.ITEM3)) + + assertEquals("456", bar1(BigEnum.ITEM4)) + assertEquals("456", bar1(BigEnum.ITEM5)) + assertEquals("456", bar1(BigEnum.ITEM6)) + + assertEquals("-1", bar1(BigEnum.ITEM7)) + + //bar2 + assertEquals("789", bar2(BigEnum.ITEM7)) + assertEquals("789", bar2(BigEnum.ITEM8)) + assertEquals("789", bar2(BigEnum.ITEM9)) + + assertEquals("10", bar2(BigEnum.ITEM10)) + + assertEquals("1112", bar2(BigEnum.ITEM11)) + assertEquals("1112", bar2(BigEnum.ITEM12)) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/duplicatingItems.kt b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/duplicatingItems.kt new file mode 100644 index 00000000000..29ecaeaceee --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/duplicatingItems.kt @@ -0,0 +1,25 @@ +import kotlin.test.assertEquals + +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +fun bar(x : Season) : String { + when (x) { + Season.WINTER, Season.SPRING -> return "winter_spring" + Season.SUMMER, Season.SPRING -> return "summer" + else -> return "autumn" + } +} + +fun box() : String { + assertEquals("winter_spring", bar(Season.WINTER)) + assertEquals("winter_spring", bar(Season.SPRING)) + assertEquals("summer", bar(Season.SUMMER)) + assertEquals("autumn", bar(Season.AUTUMN)) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/enumInsideClassObject.kt b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/enumInsideClassObject.kt new file mode 100644 index 00000000000..b1218e83ded --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/enumInsideClassObject.kt @@ -0,0 +1,29 @@ +import kotlin.test.assertEquals + +class A { + class object { + enum class Season { + WINTER + SPRING + SUMMER + AUTUMN + } + } +} + +fun foo(x : A.Season) : String { + return when (x) { + A.Season.WINTER -> "winter" + A.Season.SPRING -> "spring" + A.Season.SUMMER -> "summer" + else -> "other" + } +} + +fun box() : String { + assertEquals("winter", foo(A.Season.WINTER)) + assertEquals("spring", foo(A.Season.SPRING)) + assertEquals("summer", foo(A.Season.SUMMER)) + assertEquals("other", foo(A.Season.AUTUMN)) + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/expression.kt b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/expression.kt new file mode 100644 index 00000000000..02b0a1dd73f --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/expression.kt @@ -0,0 +1,37 @@ +import kotlin.test.assertEquals + +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +fun bar1(x : Season) : String { + return when (x) { + Season.WINTER, Season.SPRING -> "winter_spring" + Season.SUMMER -> "summer" + else -> "autumn" + } +} + +fun bar2(x : Season) : String { + return when (x) { + Season.WINTER, Season.SPRING -> "winter_spring" + Season.SUMMER -> "summer" + Season.AUTUMN -> "autumn" + } +} + +fun box() : String { + assertEquals("winter_spring", bar1(Season.WINTER)) + assertEquals("winter_spring", bar1(Season.SPRING)) + assertEquals("summer", bar1(Season.SUMMER)) + assertEquals("autumn", bar1(Season.AUTUMN)) + + assertEquals("winter_spring", bar2(Season.WINTER)) + assertEquals("winter_spring", bar2(Season.SPRING)) + assertEquals("summer", bar2(Season.SUMMER)) + assertEquals("autumn", bar2(Season.AUTUMN)) + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/functionLiteralInTopLevel.kt b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/functionLiteralInTopLevel.kt new file mode 100644 index 00000000000..d525f7bcb56 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/functionLiteralInTopLevel.kt @@ -0,0 +1,19 @@ +import kotlin.test.assertEquals + +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +fun foo(x : Season, block : (Season) -> String) = block(x) + +fun box() : String { + return foo(Season.SPRING) { + x -> when (x) { + Season.SPRING -> "OK" + else -> "fail" + } + } +} diff --git a/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/manyWhensWithinClass.kt b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/manyWhensWithinClass.kt new file mode 100644 index 00000000000..90aa1b49db1 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/manyWhensWithinClass.kt @@ -0,0 +1,50 @@ +import kotlin.test.assertEquals + +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +class A { + public fun bar1(x : Season) : String { + when (x) { + Season.WINTER, Season.SPRING -> return "winter_spring" + Season.SPRING -> return "spring" + Season.SUMMER -> return "summer" + } + + return "autumn"; + } + + public fun bar2(y : Season) : String { + return bar3(y) { x -> + when (x) { + Season.WINTER, Season.SPRING -> "winter_spring" + Season.SPRING -> "spring" + Season.SUMMER -> "summer" + else -> "autumn" + } + } + } + + private fun bar3(x : Season, block : (Season) -> String) = block(x) +} + +fun box() : String { + val a = A() + + assertEquals("winter_spring", a.bar1(Season.WINTER)) + assertEquals("winter_spring", a.bar1(Season.SPRING)) + assertEquals("summer", a.bar1(Season.SUMMER)) + assertEquals("autumn", a.bar1(Season.AUTUMN)) + + assertEquals("winter_spring", a.bar2(Season.WINTER)) + assertEquals("winter_spring", a.bar2(Season.SPRING)) + assertEquals("summer", a.bar2(Season.SUMMER)) + assertEquals("autumn", a.bar2(Season.AUTUMN)) + + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/nonConstantEnum.kt b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/nonConstantEnum.kt new file mode 100644 index 00000000000..d0003a0f7de --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/nonConstantEnum.kt @@ -0,0 +1,16 @@ +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +fun foo(): Season = Season.SPRING +fun bar(): Season = Season.SPRING + +fun box() : String { + when (foo()) { + bar() -> return "OK" + else -> return "fail" + } +} diff --git a/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/subjectAny.kt b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/subjectAny.kt new file mode 100644 index 00000000000..d78568a187f --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/subjectAny.kt @@ -0,0 +1,26 @@ +import kotlin.test.assertEquals + +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +fun foo(x : Any) : String { + return when (x) { + Season.WINTER -> "winter" + Season.SPRING -> "spring" + Season.SUMMER -> "summer" + else -> "other" + } +} + +fun box() : String { + assertEquals("winter", foo(Season.WINTER)) + assertEquals("spring", foo(Season.SPRING)) + assertEquals("summer", foo(Season.SUMMER)) + assertEquals("other", foo(Season.AUTUMN)) + assertEquals("other", foo(123)) + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/withoutElse.kt b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/withoutElse.kt new file mode 100644 index 00000000000..35855390962 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/withoutElse.kt @@ -0,0 +1,41 @@ +import kotlin.test.assertEquals + +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +fun bar1(x : Season) : String { + when (x) { + Season.WINTER, Season.SPRING -> return "winter_spring" + Season.SPRING -> return "spring" + Season.SUMMER -> return "summer" + } + return "autumn" +} + +fun bar2(x : Season) : String { + when (x) { + Season.WINTER, Season.SPRING -> return "winter_spring" + Season.SPRING -> return "spring" + Season.SUMMER -> return "summer" + Season.AUTUMN -> return "autumn" + } + + return "fail unknown" +} + +fun box() : String { + assertEquals("winter_spring", bar1(Season.WINTER)) + assertEquals("winter_spring", bar1(Season.SPRING)) + assertEquals("summer", bar1(Season.SUMMER)) + assertEquals("autumn", bar1(Season.AUTUMN)) + + assertEquals("winter_spring", bar2(Season.WINTER)) + assertEquals("winter_spring", bar2(Season.SPRING)) + assertEquals("summer", bar2(Season.SUMMER)) + assertEquals("autumn", bar2(Season.AUTUMN)) + return "OK" +} diff --git a/compiler/testData/codegen/bytecodeText/whenEnumOptimization/bigEnum.kt b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/bigEnum.kt new file mode 100644 index 00000000000..f930ed27622 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/bigEnum.kt @@ -0,0 +1,26 @@ +import kotlin.test.assertEquals + +enum class BigEnum { + ITEM1 ITEM2 ITEM3 ITEM4 ITEM5 ITEM6 ITEM7 ITEM8 ITEM9 ITEM10 ITEM11 ITEM12 ITEM13 ITEM14 ITEM15 ITEM16 ITEM17 ITEM18 ITEM19 ITEM20 +} + +fun bar1(x : BigEnum) : String { + when (x) { + BigEnum.ITEM1, BigEnum.ITEM2, BigEnum.ITEM3 -> return "123" + BigEnum.ITEM4, BigEnum.ITEM5, BigEnum.ITEM6 -> return "456" + } + + return "-1"; + +} + +fun bar2(x : BigEnum) : String { + when (x) { + BigEnum.ITEM7, BigEnum.ITEM8, BigEnum.ITEM9 -> return "789" + BigEnum.ITEM10 -> return "10" + BigEnum.ITEM11, BigEnum.ITEM12 -> return "1112" + else -> return "-1" + } +} + +// 2 TABLESWITCH diff --git a/compiler/testData/codegen/bytecodeText/whenEnumOptimization/duplicatingItems.kt b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/duplicatingItems.kt new file mode 100644 index 00000000000..c9d73a41f3a --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/duplicatingItems.kt @@ -0,0 +1,18 @@ +import kotlin.test.assertEquals + +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +fun bar(x : Season) : String { + when (x) { + Season.WINTER, Season.SPRING -> return "winter_spring" + Season.SUMMER, Season.SPRING -> return "summer" + else -> return "autumn" + } +} + +// 1 TABLESWITCH diff --git a/compiler/testData/codegen/bytecodeText/whenEnumOptimization/expression.kt b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/expression.kt new file mode 100644 index 00000000000..4b158c5472b --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/expression.kt @@ -0,0 +1,27 @@ +import kotlin.test.assertEquals + +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +fun bar1(x : Season) : String { + return when (x) { + Season.WINTER, Season.SPRING -> "winter_spring" + Season.SUMMER -> "summer" + else -> "autumn" + } +} + +fun bar2(x : Season) : String { + return when (x) { + Season.WINTER, Season.SPRING -> "winter_spring" + Season.SUMMER -> "summer" + Season.AUTUMN -> "autumn" + } +} + +// 2 TABLESWITCH +// 1 @_DefaultPackage-expression-.*\$WhenMappings\.class diff --git a/compiler/testData/codegen/bytecodeText/whenEnumOptimization/functionLiteralInTopLevel.kt b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/functionLiteralInTopLevel.kt new file mode 100644 index 00000000000..065bf5213da --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/functionLiteralInTopLevel.kt @@ -0,0 +1,22 @@ +import kotlin.test.assertEquals + +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +fun foo(x : Season, block : (Season) -> String) = block(x) + +fun box() : String { + return foo(Season.SPRING) { + x -> when (x) { + Season.SPRING -> "OK" + else -> "fail" + } + } +} + +// 1 LOOKUPSWITCH +// 1 @_DefaultPackage-functionLiteralInTopLevel-[a-z0-9]+\$WhenMappings.class diff --git a/compiler/testData/codegen/bytecodeText/whenEnumOptimization/manyWhensWithinClass.kt b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/manyWhensWithinClass.kt new file mode 100644 index 00000000000..5a4d8f18b2c --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/manyWhensWithinClass.kt @@ -0,0 +1,36 @@ +package abc.foo + +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +class A { + public fun bar1(x : Season) : String { + when (x) { + Season.WINTER, Season.SPRING -> return "winter_spring" + Season.SPRING -> return "spring" + Season.SUMMER -> return "summer" + } + + return "autumn"; + } + + public fun bar2(y : Season) : String { + return bar3(y) { x -> + when (x) { + Season.WINTER, Season.SPRING -> "winter_spring" + Season.SPRING -> "spring" + Season.SUMMER -> "summer" + else -> "autumn" + } + } + } + + private fun bar3(x : Season, block : (Season) -> String) = block(x) +} + +// 2 TABLESWITCH +// 1 @abc/foo/A\$WhenMappings\.class diff --git a/compiler/testData/codegen/bytecodeText/whenEnumOptimization/nonConstantEnum.kt b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/nonConstantEnum.kt new file mode 100644 index 00000000000..8d0b8f683a6 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/nonConstantEnum.kt @@ -0,0 +1,18 @@ +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +fun foo(): Season = Season.SPRING +fun bar(): Season = Season.SPRING + +fun box() : String { + when (foo()) { + bar() -> return "OK" + else -> return "fail" + } +} + +// 0 TABLESWITCH diff --git a/compiler/testData/codegen/bytecodeText/whenEnumOptimization/subjectAny.kt b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/subjectAny.kt new file mode 100644 index 00000000000..5adf4b68c55 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/subjectAny.kt @@ -0,0 +1,18 @@ +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +fun foo(x : Any) : String { + return when (x) { + Season.WINTER -> "winter" + Season.SPRING -> "spring" + Season.SUMMER -> "summer" + else -> "other" + } +} + +// 0 TABLESWITCH +// 0 LOOKUPSWITCH diff --git a/compiler/testData/codegen/bytecodeText/whenEnumOptimization/withoutElse.kt b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/withoutElse.kt new file mode 100644 index 00000000000..6b0960fc52a --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/whenEnumOptimization/withoutElse.kt @@ -0,0 +1,31 @@ +import kotlin.test.assertEquals + +enum class Season { + WINTER + SPRING + SUMMER + AUTUMN +} + +fun bar1(x : Season) : String { + when (x) { + Season.WINTER, Season.SPRING -> return "winter_spring" + Season.SPRING -> return "spring" + Season.SUMMER -> return "summer" + } + return "autumn" +} + +fun bar2(x : Season) : String { + when (x) { + Season.WINTER, Season.SPRING -> return "winter_spring" + Season.SPRING -> return "spring" + Season.SUMMER -> return "summer" + Season.AUTUMN -> return "autumn" + } + + return "fail unknown" +} + +// 2 TABLESWITCH +// 1 @_DefaultPackage-withoutElse-.*\$WhenMappings\.class diff --git a/compiler/tests/org/jetbrains/jet/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/BytecodeTextTestGenerated.java index b8a37d0332c..8dde716183d 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/BytecodeTextTestGenerated.java @@ -31,7 +31,7 @@ import org.jetbrains.jet.codegen.AbstractBytecodeTextTest; /** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") @TestMetadata("compiler/testData/codegen/bytecodeText") -@InnerTestClasses({BytecodeTextTestGenerated.BoxingOptimization.class, BytecodeTextTestGenerated.Constants.class, BytecodeTextTestGenerated.DirectInvoke.class, BytecodeTextTestGenerated.Statements.class}) +@InnerTestClasses({BytecodeTextTestGenerated.BoxingOptimization.class, BytecodeTextTestGenerated.Constants.class, BytecodeTextTestGenerated.DirectInvoke.class, BytecodeTextTestGenerated.Statements.class, BytecodeTextTestGenerated.WhenEnumOptimization.class}) public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { @TestMetadata("accessorForProtected.kt") public void testAccessorForProtected() throws Exception { @@ -304,6 +304,54 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { } + @TestMetadata("compiler/testData/codegen/bytecodeText/whenEnumOptimization") + public static class WhenEnumOptimization extends AbstractBytecodeTextTest { + public void testAllFilesPresentInWhenEnumOptimization() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/bytecodeText/whenEnumOptimization"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("bigEnum.kt") + public void testBigEnum() throws Exception { + doTest("compiler/testData/codegen/bytecodeText/whenEnumOptimization/bigEnum.kt"); + } + + @TestMetadata("duplicatingItems.kt") + public void testDuplicatingItems() throws Exception { + doTest("compiler/testData/codegen/bytecodeText/whenEnumOptimization/duplicatingItems.kt"); + } + + @TestMetadata("expression.kt") + public void testExpression() throws Exception { + doTest("compiler/testData/codegen/bytecodeText/whenEnumOptimization/expression.kt"); + } + + @TestMetadata("functionLiteralInTopLevel.kt") + public void testFunctionLiteralInTopLevel() throws Exception { + doTest("compiler/testData/codegen/bytecodeText/whenEnumOptimization/functionLiteralInTopLevel.kt"); + } + + @TestMetadata("manyWhensWithinClass.kt") + public void testManyWhensWithinClass() throws Exception { + doTest("compiler/testData/codegen/bytecodeText/whenEnumOptimization/manyWhensWithinClass.kt"); + } + + @TestMetadata("nonConstantEnum.kt") + public void testNonConstantEnum() throws Exception { + doTest("compiler/testData/codegen/bytecodeText/whenEnumOptimization/nonConstantEnum.kt"); + } + + @TestMetadata("subjectAny.kt") + public void testSubjectAny() throws Exception { + doTest("compiler/testData/codegen/bytecodeText/whenEnumOptimization/subjectAny.kt"); + } + + @TestMetadata("withoutElse.kt") + public void testWithoutElse() throws Exception { + doTest("compiler/testData/codegen/bytecodeText/whenEnumOptimization/withoutElse.kt"); + } + + } + public static Test suite() { TestSuite suite = new TestSuite("BytecodeTextTestGenerated"); suite.addTestSuite(BytecodeTextTestGenerated.class); @@ -311,6 +359,7 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { suite.addTestSuite(Constants.class); suite.addTestSuite(DirectInvoke.class); suite.addTestSuite(Statements.class); + suite.addTestSuite(WhenEnumOptimization.class); return suite; } } diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index f9fa1e95ff6..6183101a18e 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -31,7 +31,7 @@ import org.jetbrains.jet.codegen.generated.AbstractBlackBoxCodegenTest; /** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ @SuppressWarnings("all") @TestMetadata("compiler/testData/codegen/boxWithStdlib") -@InnerTestClasses({BlackBoxWithStdlibCodegenTestGenerated.Annotations.class, BlackBoxWithStdlibCodegenTestGenerated.Arrays.class, BlackBoxWithStdlibCodegenTestGenerated.BoxingOptimization.class, BlackBoxWithStdlibCodegenTestGenerated.CallableReference.class, BlackBoxWithStdlibCodegenTestGenerated.Casts.class, BlackBoxWithStdlibCodegenTestGenerated.DataClasses.class, BlackBoxWithStdlibCodegenTestGenerated.Evaluate.class, BlackBoxWithStdlibCodegenTestGenerated.FullJdk.class, BlackBoxWithStdlibCodegenTestGenerated.HashPMap.class, BlackBoxWithStdlibCodegenTestGenerated.JdkAnnotations.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformNames.class, BlackBoxWithStdlibCodegenTestGenerated.Ranges.class, BlackBoxWithStdlibCodegenTestGenerated.Reflection.class, BlackBoxWithStdlibCodegenTestGenerated.Regressions.class, BlackBoxWithStdlibCodegenTestGenerated.Strings.class, BlackBoxWithStdlibCodegenTestGenerated.ToArray.class, BlackBoxWithStdlibCodegenTestGenerated.Vararg.class, BlackBoxWithStdlibCodegenTestGenerated.When.class}) +@InnerTestClasses({BlackBoxWithStdlibCodegenTestGenerated.Annotations.class, BlackBoxWithStdlibCodegenTestGenerated.Arrays.class, BlackBoxWithStdlibCodegenTestGenerated.BoxingOptimization.class, BlackBoxWithStdlibCodegenTestGenerated.CallableReference.class, BlackBoxWithStdlibCodegenTestGenerated.Casts.class, BlackBoxWithStdlibCodegenTestGenerated.DataClasses.class, BlackBoxWithStdlibCodegenTestGenerated.Evaluate.class, BlackBoxWithStdlibCodegenTestGenerated.FullJdk.class, BlackBoxWithStdlibCodegenTestGenerated.HashPMap.class, BlackBoxWithStdlibCodegenTestGenerated.JdkAnnotations.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformNames.class, BlackBoxWithStdlibCodegenTestGenerated.Ranges.class, BlackBoxWithStdlibCodegenTestGenerated.Reflection.class, BlackBoxWithStdlibCodegenTestGenerated.Regressions.class, BlackBoxWithStdlibCodegenTestGenerated.Strings.class, BlackBoxWithStdlibCodegenTestGenerated.ToArray.class, BlackBoxWithStdlibCodegenTestGenerated.Vararg.class, BlackBoxWithStdlibCodegenTestGenerated.When.class, BlackBoxWithStdlibCodegenTestGenerated.WhenEnumOptimization.class}) public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public void testAllFilesPresentInBoxWithStdlib() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxWithStdlib"), Pattern.compile("^(.+)\\.kt$"), true); @@ -1952,6 +1952,59 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/whenEnumOptimization") + public static class WhenEnumOptimization extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInWhenEnumOptimization() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxWithStdlib/whenEnumOptimization"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("bigEnum.kt") + public void testBigEnum() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/bigEnum.kt"); + } + + @TestMetadata("duplicatingItems.kt") + public void testDuplicatingItems() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/duplicatingItems.kt"); + } + + @TestMetadata("enumInsideClassObject.kt") + public void testEnumInsideClassObject() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/enumInsideClassObject.kt"); + } + + @TestMetadata("expression.kt") + public void testExpression() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/expression.kt"); + } + + @TestMetadata("functionLiteralInTopLevel.kt") + public void testFunctionLiteralInTopLevel() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/functionLiteralInTopLevel.kt"); + } + + @TestMetadata("manyWhensWithinClass.kt") + public void testManyWhensWithinClass() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/manyWhensWithinClass.kt"); + } + + @TestMetadata("nonConstantEnum.kt") + public void testNonConstantEnum() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/nonConstantEnum.kt"); + } + + @TestMetadata("subjectAny.kt") + public void testSubjectAny() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/subjectAny.kt"); + } + + @TestMetadata("withoutElse.kt") + public void testWithoutElse() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/whenEnumOptimization/withoutElse.kt"); + } + + } + public static Test suite() { TestSuite suite = new TestSuite("BlackBoxWithStdlibCodegenTestGenerated"); suite.addTestSuite(BlackBoxWithStdlibCodegenTestGenerated.class); @@ -1973,6 +2026,7 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode suite.addTestSuite(ToArray.class); suite.addTestSuite(Vararg.class); suite.addTestSuite(When.class); + suite.addTestSuite(WhenEnumOptimization.class); return suite; } }