diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index ca87da3a2df..7b61474a78a 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -37,6 +37,7 @@ import org.jetbrains.jet.codegen.inline.NameGenerator; import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethod; import org.jetbrains.jet.codegen.state.GenerationState; import org.jetbrains.jet.codegen.state.JetTypeMapper; +import org.jetbrains.jet.codegen.when.SwitchCodegen; import org.jetbrains.jet.codegen.when.SwitchCodegenUtil; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; @@ -112,7 +113,7 @@ public class ExpressionCodegen extends JetVisitor implem * When we create a temporary variable to hold some value not to compute it many times * we put it into this map to emit access to that variable instead of evaluating the whole expression */ - final Map tempVariables = Maps.newHashMap(); + public final Map tempVariables = Maps.newHashMap(); private int myLastLineNumber = -1; @@ -3725,8 +3726,10 @@ The "returned" value of try expression with no finally is either the last expres Type resultType = isStatement ? Type.VOID_TYPE : expressionType(expression); - if (SwitchCodegenUtil.canSwitchBeUsedIn(expression, subjectType, bindingContext)) { - return generateSwitch(expression, resultType, isStatement); + SwitchCodegen switchCodegen = SwitchCodegenUtil.buildAppropriateSwitchCodegenIfPossible(expression, isStatement, this); + if (switchCodegen != null) { + switchCodegen.generate(); + return StackValue.onStack(resultType); } int subjectLocal = expr != null ? myFrameMap.enterTemp(subjectType) : -1; @@ -3816,19 +3819,6 @@ The "returned" value of try expression with no finally is either the last expres } } - private StackValue generateSwitch( - @NotNull JetWhenExpression expression, - @NotNull Type resultType, - boolean isStatement - ) { - SwitchCodegenUtil.buildAppropriateSwitchCodegen( - expression, isStatement, this - - ).generate(); - - return StackValue.onStack(resultType); - } - private boolean isIntRangeExpr(JetExpression rangeExpression) { if (rangeExpression instanceof JetBinaryExpression) { JetBinaryExpression binaryExpression = (JetBinaryExpression) rangeExpression; diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/when/StringSwitchCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/when/StringSwitchCodegen.java new file mode 100644 index 00000000000..db5b334c104 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/when/StringSwitchCodegen.java @@ -0,0 +1,122 @@ +/* + * 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.google.common.collect.Maps; +import com.intellij.openapi.util.Pair; +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.StringValue; +import org.jetbrains.org.objectweb.asm.Label; +import org.jetbrains.org.objectweb.asm.Type; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class StringSwitchCodegen extends SwitchCodegen { + private static final String HASH_CODE_METHOD_DESC = Type.getMethodDescriptor(Type.INT_TYPE); + private static final String EQUALS_METHOD_DESC = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Object.class)); + + private final Map>> hashCodesToStringAndEntryLabel = Maps.newHashMap(); + private int tempVarIndex; + + public StringSwitchCodegen( + @NotNull JetWhenExpression expression, + boolean isStatement, + @NotNull ExpressionCodegen codegen + ) { + super(expression, isStatement, codegen); + } + + @Override + protected void processConstant( + @NotNull CompileTimeConstant constant, @NotNull Label entryLabel + ) { + assert constant instanceof StringValue : "guaranteed by usage contract"; + int hashCode = constant.hashCode(); + + if (!transitionsTable.containsKey(hashCode)) { + transitionsTable.put(hashCode, new Label()); + hashCodesToStringAndEntryLabel.put(hashCode, new ArrayList>()); + } + + hashCodesToStringAndEntryLabel.get(hashCode).add( + new Pair(((StringValue) constant).getValue(), entryLabel) + ); + } + + @Override + public void generate() { + super.generate(); + codegen.myFrameMap.leaveTemp(subjectType); + } + + @Override + protected void generateSubject() { + tempVarIndex = codegen.myFrameMap.enterTemp(subjectType); + super.generateSubject(); + v.store(tempVarIndex, subjectType); + + v.load(tempVarIndex, subjectType); + v.invokevirtual( + subjectType.getInternalName(), + "hashCode", HASH_CODE_METHOD_DESC, false + ); + } + + @Override + protected void generateEntries() { + for (int hashCode : hashCodesToStringAndEntryLabel.keySet()) { + v.visitLabel(transitionsTable.get(hashCode)); + + List> items = hashCodesToStringAndEntryLabel.get(hashCode); + Label nextLabel = null; + + for (int i = 0; i < items.size(); i++) { + if (nextLabel != null) { + v.visitLabel(nextLabel); + } + + Pair stringAndEntryLabel = items.get(i); + + v.load(tempVarIndex, subjectType); + v.aconst(stringAndEntryLabel.first); + v.invokevirtual( + subjectType.getInternalName(), + "equals", + EQUALS_METHOD_DESC, + false + ); + + if (i + 1 < items.size()) { + nextLabel = new Label(); + } + else { + nextLabel = defaultLabel; + } + + v.ifeq(nextLabel); + v.goTo(stringAndEntryLabel.getSecond()); + } + } + + super.generateEntries(); + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/when/SwitchCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/when/SwitchCodegen.java index 7f46cafca8e..d4ffa1158b9 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/when/SwitchCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/when/SwitchCodegen.java @@ -19,7 +19,8 @@ package org.jetbrains.jet.codegen.when; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.codegen.ExpressionCodegen; import org.jetbrains.jet.codegen.FrameMap; -import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.psi.JetWhenEntry; +import org.jetbrains.jet.lang.psi.JetWhenExpression; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.org.objectweb.asm.Label; @@ -41,6 +42,7 @@ abstract public class SwitchCodegen { protected final Collection