001 /* 002 * Copyright 2010-2014 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.jet.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.jet.codegen.AsmUtil; 023 import org.jetbrains.jet.codegen.RangeCodegenUtil; 024 import org.jetbrains.jet.codegen.optimization.common.OptimizationBasicInterpreter; 025 import org.jetbrains.jet.lang.resolve.java.JvmPrimitiveType; 026 import org.jetbrains.jet.lang.resolve.name.FqName; 027 import org.jetbrains.jet.lang.types.lang.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 private BoxedBasicValue 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 methodInsnNode = (MethodInsnNode) insn; 148 149 return isWrapperClassName(methodInsnNode.owner) && "valueOf".equals(methodInsnNode.name); 150 } 151 152 private static boolean isNextMethodCallOfProgressionIterator( 153 @NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values 154 ) { 155 return (insn.getOpcode() == Opcodes.INVOKEINTERFACE && 156 values.get(0) instanceof ProgressionIteratorBasicValue && 157 "next".equals(((MethodInsnNode) insn).name)); 158 } 159 160 private static boolean isIteratorMethodCallOfProgression( 161 @NotNull AbstractInsnNode insn, @NotNull List<? extends BasicValue> values 162 ) { 163 return (insn.getOpcode() == Opcodes.INVOKEINTERFACE && 164 values.get(0).getType() != null && 165 isProgressionClass(values.get(0).getType().getInternalName()) && 166 "iterator".equals(((MethodInsnNode) insn).name)); 167 } 168 169 private static boolean isProgressionClass(String internalClassName) { 170 return RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(internalClassName)); 171 } 172 173 /** 174 * e.g. for "kotlin/IntRange" it returns "Int" 175 * 176 * @param progressionClassInternalName 177 * @return 178 * @throws java.lang.AssertionError if progressionClassInternalName is not progression class internal name 179 */ 180 @NotNull 181 private static String getValuesTypeOfProgressionClass(String progressionClassInternalName) { 182 PrimitiveType type = RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType( 183 buildFqNameByInternal(progressionClassInternalName) 184 ); 185 186 assert type != null : "type should be not null"; 187 188 return type.getTypeName().asString(); 189 } 190 191 @Override 192 public BasicValue unaryOperation(@NotNull AbstractInsnNode insn, @NotNull BasicValue value) throws AnalyzerException { 193 if (insn.getOpcode() == Opcodes.CHECKCAST && isExactValue(value)) { 194 return value; 195 } 196 197 return super.unaryOperation(insn, value); 198 } 199 200 private static boolean isExactValue(@NotNull BasicValue value) { 201 return value instanceof ProgressionIteratorBasicValue || 202 value instanceof BoxedBasicValue || 203 (value.getType() != null && isProgressionClass(value.getType().getInternalName())); 204 } 205 206 @Override 207 @NotNull 208 public BasicValue merge(@NotNull BasicValue v, @NotNull BasicValue w) { 209 if (v instanceof BoxedBasicValue && ((BoxedBasicValue) v).typeEquals(w)) { 210 onMergeSuccess((BoxedBasicValue) v, (BoxedBasicValue) w); 211 return v; 212 } 213 214 if (v instanceof BoxedBasicValue && w == BasicValue.UNINITIALIZED_VALUE) { 215 return v; 216 } 217 218 if (w instanceof BoxedBasicValue && v == BasicValue.UNINITIALIZED_VALUE) { 219 return w; 220 } 221 222 if (v instanceof BoxedBasicValue) { 223 onMergeFail((BoxedBasicValue) v); 224 v = new BasicValue(v.getType()); 225 } 226 227 if (w instanceof BoxedBasicValue) { 228 onMergeFail((BoxedBasicValue) w); 229 w = new BasicValue(w.getType()); 230 } 231 232 return super.merge(v, w); 233 } 234 235 protected void onNewBoxedValue(@NotNull BoxedBasicValue value) { 236 237 } 238 239 protected void onUnboxing(@NotNull AbstractInsnNode insn, @NotNull BoxedBasicValue value, @NotNull Type resultType) { 240 241 } 242 243 protected void onMethodCallWithBoxedValue(@NotNull BoxedBasicValue value) { 244 245 } 246 247 protected void onMergeFail(@NotNull BoxedBasicValue value) { 248 249 } 250 251 protected void onMergeSuccess(@NotNull BoxedBasicValue v, @NotNull BoxedBasicValue w) { 252 253 } 254 }