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 }