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);
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    }