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.JetWhenExpression;
024    import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
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 JetWhenExpression expression,
042                boolean isStatement,
043                @NotNull ExpressionCodegen codegen
044        ) {
045            super(expression, isStatement, codegen);
046        }
047    
048        @Override
049        protected void processConstant(
050                @NotNull CompileTimeConstant constant, @NotNull Label entryLabel
051        ) {
052            assert constant instanceof StringValue : "guaranteed by usage contract";
053            int hashCode = constant.hashCode();
054    
055            if (!transitionsTable.containsKey(hashCode)) {
056                transitionsTable.put(hashCode, new Label());
057                hashCodesToStringAndEntryLabel.put(hashCode, new ArrayList<Pair<String, Label>>());
058            }
059    
060            hashCodesToStringAndEntryLabel.get(hashCode).add(
061                    new Pair<String, Label>(((StringValue) constant).getValue(), entryLabel)
062            );
063        }
064    
065        @Override
066        public void generate() {
067            super.generate();
068            codegen.myFrameMap.leaveTemp(subjectType);
069        }
070    
071        @Override
072        protected void generateSubject() {
073            tempVarIndex = codegen.myFrameMap.enterTemp(subjectType);
074            super.generateSubject();
075            v.store(tempVarIndex, subjectType);
076    
077            v.load(tempVarIndex, subjectType);
078    
079            generateNullCheckIfNeeded();
080    
081            v.invokevirtual(
082                    subjectType.getInternalName(),
083                    "hashCode", HASH_CODE_METHOD_DESC, false
084            );
085        }
086    
087        @Override
088        protected void generateEntries() {
089            for (int hashCode : hashCodesToStringAndEntryLabel.keySet()) {
090                v.visitLabel(transitionsTable.get(hashCode));
091    
092                List<Pair<String, Label>> items = hashCodesToStringAndEntryLabel.get(hashCode);
093                Label nextLabel = null;
094    
095                for (int i = 0; i < items.size(); i++) {
096                    if (nextLabel != null) {
097                        v.visitLabel(nextLabel);
098                    }
099    
100                    Pair<String, Label> stringAndEntryLabel = items.get(i);
101    
102                    v.load(tempVarIndex, subjectType);
103                    v.aconst(stringAndEntryLabel.first);
104                    v.invokevirtual(
105                            subjectType.getInternalName(),
106                            "equals",
107                            EQUALS_METHOD_DESC,
108                            false
109                    );
110    
111                    if (i + 1 < items.size()) {
112                        nextLabel = new Label();
113                    }
114                    else {
115                        nextLabel = defaultLabel;
116                    }
117    
118                    v.ifeq(nextLabel);
119                    v.goTo(stringAndEntryLabel.getSecond());
120                }
121            }
122    
123            super.generateEntries();
124        }
125    }