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.optimization.boxing; 018 019 import com.google.common.collect.ImmutableSet; 020 import org.jetbrains.annotations.NotNull; 021 import org.jetbrains.annotations.Nullable; 022 import org.jetbrains.kotlin.codegen.AsmUtil; 023 import org.jetbrains.kotlin.codegen.RangeCodegenUtil; 024 import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter; 025 import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType; 026 import org.jetbrains.kotlin.name.FqName; 027 import org.jetbrains.kotlin.builtins.PrimitiveType; 028 import org.jetbrains.org.objectweb.asm.Opcodes; 029 import org.jetbrains.org.objectweb.asm.Type; 030 import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode; 031 import org.jetbrains.org.objectweb.asm.tree.InsnList; 032 import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode; 033 import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException; 034 import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue; 035 036 import java.util.HashMap; 037 import java.util.List; 038 import java.util.Map; 039 040 public class BoxingInterpreter extends OptimizationBasicInterpreter { 041 private static final ImmutableSet<String> UNBOXING_METHOD_NAMES; 042 043 static { 044 UNBOXING_METHOD_NAMES = ImmutableSet.of( 045 "booleanValue", "charValue", "byteValue", "shortValue", "intValue", "floatValue", "longValue", "doubleValue" 046 ); 047 } 048 049 050 private final Map<Integer, BoxedBasicValue> boxingPlaces = new HashMap<Integer, BoxedBasicValue>(); 051 private final InsnList insnList; 052 053 public BoxingInterpreter(InsnList insnList) { 054 this.insnList = insnList; 055 } 056 057 @NotNull 058 protected BasicValue createNewBoxing( 059 @NotNull AbstractInsnNode insn, @NotNull Type type, 060 @Nullable ProgressionIteratorBasicValue progressionIterator 061 ) { 062 int index = insnList.indexOf(insn); 063 if (!boxingPlaces.containsKey(index)) { 064 BoxedBasicValue boxedBasicValue = new BoxedBasicValue(type, insn, progressionIterator); 065 onNewBoxedValue(boxedBasicValue); 066 boxingPlaces.put(index, boxedBasicValue); 067 } 068 069 return boxingPlaces.get(index); 070 } 071 072 @Override 073 @Nullable 074 public BasicValue naryOperation(@NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values) throws AnalyzerException { 075 BasicValue value = super.naryOperation(insn, values); 076 077 if (values.isEmpty()) return value; 078 079 BasicValue firstArg = values.get(0); 080 081 if (isBoxing(insn)) { 082 return createNewBoxing(insn, value.getType(), null); 083 } 084 else if (isUnboxing(insn) && 085 firstArg instanceof BoxedBasicValue) { 086 onUnboxing(insn, (BoxedBasicValue) firstArg, value.getType()); 087 } 088 else if (isIteratorMethodCallOfProgression(insn, values)) { 089 return new ProgressionIteratorBasicValue( 090 getValuesTypeOfProgressionClass(firstArg.getType().getInternalName()) 091 ); 092 } 093 else if (isNextMethodCallOfProgressionIterator(insn, values)) { 094 assert firstArg instanceof ProgressionIteratorBasicValue : "firstArg should be progression iterator"; 095 096 ProgressionIteratorBasicValue progressionIterator = (ProgressionIteratorBasicValue) firstArg; 097 return createNewBoxing( 098 insn, 099 AsmUtil.boxType(progressionIterator.getValuesPrimitiveType()), 100 progressionIterator 101 ); 102 } 103 else { 104 // nary operation should be a method call or multinewarray 105 // arguments for multinewarray could be only numeric 106 // so if there are boxed values in args, it's not a case of multinewarray 107 for (BasicValue arg : values) { 108 if (arg instanceof BoxedBasicValue) { 109 onMethodCallWithBoxedValue((BoxedBasicValue) arg); 110 } 111 } 112 } 113 114 return value; 115 } 116 117 private static boolean isWrapperClassNameOrNumber(@NotNull String internalClassName) { 118 return isWrapperClassName(internalClassName) || internalClassName.equals(Type.getInternalName(Number.class)); 119 } 120 121 private static boolean isWrapperClassName(@NotNull String internalClassName) { 122 return JvmPrimitiveType.isWrapperClassName( 123 buildFqNameByInternal(internalClassName) 124 ); 125 } 126 127 @NotNull 128 private static FqName buildFqNameByInternal(@NotNull String internalClassName) { 129 return new FqName(Type.getObjectType(internalClassName).getClassName()); 130 } 131 132 private static boolean isUnboxing(@NotNull AbstractInsnNode insn) { 133 if (insn.getOpcode() != Opcodes.INVOKEVIRTUAL) return false; 134 135 MethodInsnNode methodInsn = (MethodInsnNode) insn; 136 137 return isWrapperClassNameOrNumber(methodInsn.owner) && isUnboxingMethodName(methodInsn.name); 138 } 139 140 private static boolean isUnboxingMethodName(@NotNull String name) { 141 return UNBOXING_METHOD_NAMES.contains(name); 142 } 143 144 private static boolean isBoxing(@NotNull AbstractInsnNode insn) { 145 if (insn.getOpcode() != Opcodes.INVOKESTATIC) return false; 146 147 MethodInsnNode node = (MethodInsnNode) insn; 148 149 return isWrapperClassName(node.owner) && "valueOf".equals(node.name) && 150 Type.getMethodDescriptor( 151 Type.getObjectType(node.owner), 152 AsmUtil.unboxType(Type.getObjectType(node.owner)) 153 ).equals(node.desc); 154 } 155 156 private static boolean isNextMethodCallOfProgressionIterator( 157 @NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values 158 ) { 159 return (insn.getOpcode() == Opcodes.INVOKEINTERFACE && 160 values.get(0) instanceof ProgressionIteratorBasicValue && 161 "next".equals(((MethodInsnNode) insn).name)); 162 } 163 164 private static boolean isIteratorMethodCallOfProgression( 165 @NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values 166 ) { 167 return (insn.getOpcode() == Opcodes.INVOKEINTERFACE && 168 values.get(0).getType() != null && 169 isProgressionClass(values.get(0).getType().getInternalName()) && 170 "iterator".equals(((MethodInsnNode) insn).name)); 171 } 172 173 private static boolean isProgressionClass(String internalClassName) { 174 return RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(internalClassName)); 175 } 176 177 /** 178 * e.g. for "kotlin/IntRange" it returns "Int" 179 * 180 * @param progressionClassInternalName 181 * @return 182 * @throws java.lang.AssertionError if progressionClassInternalName is not progression class internal name 183 */ 184 @NotNull 185 private static String getValuesTypeOfProgressionClass(String progressionClassInternalName) { 186 PrimitiveType type = RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType( 187 buildFqNameByInternal(progressionClassInternalName) 188 ); 189 190 assert type != null : "type should be not null"; 191 192 return type.getTypeName().asString(); 193 } 194 195 @Override 196 public BasicValue unaryOperation(@NotNull AbstractInsnNode insn, @NotNull BasicValue value) throws AnalyzerException { 197 if (insn.getOpcode() == Opcodes.CHECKCAST && isExactValue(value)) { 198 return value; 199 } 200 201 return super.unaryOperation(insn, value); 202 } 203 204 protected boolean isExactValue(@NotNull BasicValue value) { 205 return value instanceof ProgressionIteratorBasicValue || 206 value instanceof BoxedBasicValue || 207 (value.getType() != null && isProgressionClass(value.getType().getInternalName())); 208 } 209 210 @Override 211 @NotNull 212 public BasicValue merge(@NotNull BasicValue v, @NotNull BasicValue w) { 213 if (v instanceof BoxedBasicValue && ((BoxedBasicValue) v).typeEquals(w)) { 214 onMergeSuccess((BoxedBasicValue) v, (BoxedBasicValue) w); 215 return v; 216 } 217 218 if (v instanceof BoxedBasicValue) { 219 onMergeFail((BoxedBasicValue) v); 220 } 221 222 if (w instanceof BoxedBasicValue) { 223 onMergeFail((BoxedBasicValue) w); 224 } 225 226 return super.merge(v, w); 227 } 228 229 protected void onNewBoxedValue(@NotNull BoxedBasicValue value) { 230 231 } 232 233 protected void onUnboxing(@NotNull AbstractInsnNode insn, @NotNull BoxedBasicValue value, @NotNull Type resultType) { 234 235 } 236 237 protected void onMethodCallWithBoxedValue(@NotNull BoxedBasicValue value) { 238 239 } 240 241 protected void onMergeFail(@NotNull BoxedBasicValue value) { 242 243 } 244 245 protected void onMergeSuccess(@NotNull BoxedBasicValue v, @NotNull BoxedBasicValue w) { 246 247 } 248 }