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 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 private static 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 }