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    }