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 }