001 /*
002 * Copyright 2010-2015 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.codegen.when;
018
019 import com.google.common.collect.Maps;
020 import com.intellij.openapi.util.Pair;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.kotlin.codegen.ExpressionCodegen;
023 import org.jetbrains.kotlin.psi.KtWhenExpression;
024 import org.jetbrains.kotlin.resolve.constants.ConstantValue;
025 import org.jetbrains.kotlin.resolve.constants.StringValue;
026 import org.jetbrains.org.objectweb.asm.Label;
027 import org.jetbrains.org.objectweb.asm.Type;
028
029 import java.util.ArrayList;
030 import java.util.List;
031 import java.util.Map;
032
033 public class StringSwitchCodegen extends SwitchCodegen {
034 private static final String HASH_CODE_METHOD_DESC = Type.getMethodDescriptor(Type.INT_TYPE);
035 private static final String EQUALS_METHOD_DESC = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Object.class));
036
037 private final Map<Integer, List<Pair<String, Label>>> hashCodesToStringAndEntryLabel = Maps.newHashMap();
038 private int tempVarIndex;
039
040 public StringSwitchCodegen(
041 @NotNull KtWhenExpression expression,
042 boolean isStatement,
043 boolean isExhaustive,
044 @NotNull ExpressionCodegen codegen
045 ) {
046 super(expression, isStatement, isExhaustive, codegen, null);
047 }
048
049 @Override
050 protected void processConstant(
051 @NotNull ConstantValue<?> constant, @NotNull Label entryLabel
052 ) {
053 assert constant instanceof StringValue : "guaranteed by usage contract";
054 int hashCode = constant.hashCode();
055
056 if (!transitionsTable.containsKey(hashCode)) {
057 transitionsTable.put(hashCode, new Label());
058 hashCodesToStringAndEntryLabel.put(hashCode, new ArrayList<Pair<String, Label>>());
059 }
060
061 hashCodesToStringAndEntryLabel.get(hashCode).add(
062 new Pair<String, Label>(((StringValue) constant).getValue(), entryLabel)
063 );
064 }
065
066 @Override
067 public void generate() {
068 super.generate();
069 codegen.myFrameMap.leaveTemp(subjectType);
070 }
071
072 @Override
073 protected void generateSubject() {
074 tempVarIndex = codegen.myFrameMap.enterTemp(subjectType);
075 super.generateSubject();
076 v.store(tempVarIndex, subjectType);
077
078 v.load(tempVarIndex, subjectType);
079
080 generateNullCheckIfNeeded();
081
082 v.invokevirtual(
083 subjectType.getInternalName(),
084 "hashCode", HASH_CODE_METHOD_DESC, false
085 );
086 }
087
088 @Override
089 protected void generateEntries() {
090 for (int hashCode : hashCodesToStringAndEntryLabel.keySet()) {
091 v.visitLabel(transitionsTable.get(hashCode));
092
093 List<Pair<String, Label>> items = hashCodesToStringAndEntryLabel.get(hashCode);
094 Label nextLabel = null;
095
096 for (int i = 0; i < items.size(); i++) {
097 if (nextLabel != null) {
098 v.visitLabel(nextLabel);
099 }
100
101 Pair<String, Label> stringAndEntryLabel = items.get(i);
102
103 v.load(tempVarIndex, subjectType);
104 v.aconst(stringAndEntryLabel.first);
105 v.invokevirtual(
106 subjectType.getInternalName(),
107 "equals",
108 EQUALS_METHOD_DESC,
109 false
110 );
111
112 if (i + 1 < items.size()) {
113 nextLabel = new Label();
114 }
115 else {
116 nextLabel = defaultLabel;
117 }
118
119 v.ifeq(nextLabel);
120 v.goTo(stringAndEntryLabel.getSecond());
121 }
122 }
123
124 super.generateEntries();
125 }
126 }