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.common;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.org.objectweb.asm.Handle;
022    import org.jetbrains.org.objectweb.asm.Opcodes;
023    import org.jetbrains.org.objectweb.asm.Type;
024    import org.jetbrains.org.objectweb.asm.tree.*;
025    import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
026    import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
027    import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter;
028    
029    import java.util.List;
030    
031    import static org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue.*;
032    
033    public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implements Opcodes {
034        private final boolean tolerantToUninitializedValues;
035    
036        public OptimizationBasicInterpreter(boolean tolerantToUninitializedValues) {
037            super(ASM5);
038            this.tolerantToUninitializedValues = tolerantToUninitializedValues;
039        }
040    
041        public OptimizationBasicInterpreter() {
042            this(false);
043        }
044    
045        @Override
046        @Nullable
047        public StrictBasicValue newValue(@Nullable Type type) {
048            if (type == null) {
049                return UNINITIALIZED_VALUE;
050            }
051    
052            switch (type.getSort()) {
053                case Type.VOID:
054                    return null;
055                case Type.INT:
056                    return INT_VALUE;
057                case Type.FLOAT:
058                    return FLOAT_VALUE;
059                case Type.LONG:
060                    return LONG_VALUE;
061                case Type.DOUBLE:
062                    return DOUBLE_VALUE;
063                case Type.BOOLEAN:
064                    return BOOLEAN_VALUE;
065                case Type.CHAR:
066                    return CHAR_VALUE;
067                case Type.BYTE:
068                    return BYTE_VALUE;
069                case Type.SHORT:
070                    return SHORT_VALUE;
071                case Type.OBJECT:
072                    return new StrictBasicValue(type);
073                case Type.ARRAY:
074                    return StrictBasicValue.REFERENCE_VALUE;
075                default:
076                    throw new IllegalArgumentException("Unknown type sort " + type.getSort());
077            }
078        }
079    
080        @Override
081        public BasicValue newOperation(@NotNull AbstractInsnNode insn) throws AnalyzerException {
082            switch (insn.getOpcode()) {
083                case ACONST_NULL:
084                    return newValue(Type.getObjectType("null"));
085                case ICONST_M1:
086                case ICONST_0:
087                case ICONST_1:
088                case ICONST_2:
089                case ICONST_3:
090                case ICONST_4:
091                case ICONST_5:
092                    return StrictBasicValue.INT_VALUE;
093                case LCONST_0:
094                case LCONST_1:
095                    return StrictBasicValue.LONG_VALUE;
096                case FCONST_0:
097                case FCONST_1:
098                case FCONST_2:
099                    return StrictBasicValue.FLOAT_VALUE;
100                case DCONST_0:
101                case DCONST_1:
102                    return StrictBasicValue.DOUBLE_VALUE;
103                case BIPUSH:
104                case SIPUSH:
105                    return StrictBasicValue.INT_VALUE;
106                case LDC:
107                    Object cst = ((LdcInsnNode) insn).cst;
108                    if (cst instanceof Integer) {
109                        return StrictBasicValue.INT_VALUE;
110                    }
111                    else if (cst instanceof Float) {
112                        return StrictBasicValue.FLOAT_VALUE;
113                    }
114                    else if (cst instanceof Long) {
115                        return StrictBasicValue.LONG_VALUE;
116                    }
117                    else if (cst instanceof Double) {
118                        return StrictBasicValue.DOUBLE_VALUE;
119                    }
120                    else if (cst instanceof String) {
121                        return newValue(Type.getObjectType("java/lang/String"));
122                    }
123                    else if (cst instanceof Type) {
124                        int sort = ((Type) cst).getSort();
125                        if (sort == Type.OBJECT || sort == Type.ARRAY) {
126                            return newValue(Type.getObjectType("java/lang/Class"));
127                        }
128                        else if (sort == Type.METHOD) {
129                            return newValue(Type.getObjectType("java/lang/invoke/MethodType"));
130                        }
131                        else {
132                            throw new IllegalArgumentException("Illegal LDC constant " + cst);
133                        }
134                    }
135                    else if (cst instanceof Handle) {
136                        return newValue(Type.getObjectType("java/lang/invoke/MethodHandle"));
137                    }
138                    else {
139                        throw new IllegalArgumentException("Illegal LDC constant " + cst);
140                    }
141                case GETSTATIC:
142                    return newValue(Type.getType(((FieldInsnNode) insn).desc));
143                case NEW:
144                    return newValue(Type.getObjectType(((TypeInsnNode) insn).desc));
145                default:
146                    throw new Error("Internal error.");
147            }
148        }
149    
150        @Override
151        public BasicValue copyOperation(@NotNull AbstractInsnNode insn, BasicValue value) throws AnalyzerException {
152            return value;
153        }
154    
155        @Override
156        public BasicValue binaryOperation(
157                @NotNull AbstractInsnNode insn,
158                @NotNull BasicValue value1,
159                @NotNull BasicValue value2
160        ) throws AnalyzerException {
161            switch (insn.getOpcode()) {
162                case IALOAD:
163                case BALOAD:
164                case CALOAD:
165                case SALOAD:
166                case IADD:
167                case ISUB:
168                case IMUL:
169                case IDIV:
170                case IREM:
171                case ISHL:
172                case ISHR:
173                case IUSHR:
174                case IAND:
175                case IOR:
176                case IXOR:
177                    return StrictBasicValue.INT_VALUE;
178                case FALOAD:
179                case FADD:
180                case FSUB:
181                case FMUL:
182                case FDIV:
183                case FREM:
184                    return StrictBasicValue.FLOAT_VALUE;
185                case LALOAD:
186                case LADD:
187                case LSUB:
188                case LMUL:
189                case LDIV:
190                case LREM:
191                case LSHL:
192                case LSHR:
193                case LUSHR:
194                case LAND:
195                case LOR:
196                case LXOR:
197                    return StrictBasicValue.LONG_VALUE;
198                case DALOAD:
199                case DADD:
200                case DSUB:
201                case DMUL:
202                case DDIV:
203                case DREM:
204                    return StrictBasicValue.DOUBLE_VALUE;
205                case AALOAD:
206                    return StrictBasicValue.REFERENCE_VALUE;
207                case LCMP:
208                case FCMPL:
209                case FCMPG:
210                case DCMPL:
211                case DCMPG:
212                    return StrictBasicValue.INT_VALUE;
213                case IF_ICMPEQ:
214                case IF_ICMPNE:
215                case IF_ICMPLT:
216                case IF_ICMPGE:
217                case IF_ICMPGT:
218                case IF_ICMPLE:
219                case IF_ACMPEQ:
220                case IF_ACMPNE:
221                case PUTFIELD:
222                    return null;
223                default:
224                    throw new Error("Internal error.");
225            }
226        }
227    
228        @Override
229        public BasicValue ternaryOperation(
230                AbstractInsnNode insn, BasicValue value1, BasicValue value2, BasicValue value3
231        ) throws AnalyzerException {
232            return null;
233        }
234    
235        @Override
236        public BasicValue naryOperation(
237                AbstractInsnNode insn, List<? extends BasicValue> values
238        ) throws AnalyzerException {
239            int opcode = insn.getOpcode();
240            if (opcode == MULTIANEWARRAY) {
241                return newValue(Type.getType(((MultiANewArrayInsnNode) insn).desc));
242            }
243            else if (opcode == INVOKEDYNAMIC) {
244                return newValue(Type.getReturnType(((InvokeDynamicInsnNode) insn).desc));
245            }
246            else {
247                return newValue(Type.getReturnType(((MethodInsnNode) insn).desc));
248            }
249        }
250    
251        @Override
252        public void returnOperation(
253                AbstractInsnNode insn, BasicValue value, BasicValue expected
254        ) throws AnalyzerException {
255        }
256    
257        @Override
258        public BasicValue unaryOperation(
259                AbstractInsnNode insn,
260                BasicValue value
261        ) throws AnalyzerException {
262            switch (insn.getOpcode()) {
263                case INEG:
264                case IINC:
265                case L2I:
266                case F2I:
267                case D2I:
268                case I2B:
269                case I2C:
270                case I2S:
271                    return StrictBasicValue.INT_VALUE;
272                case FNEG:
273                case I2F:
274                case L2F:
275                case D2F:
276                    return StrictBasicValue.FLOAT_VALUE;
277                case LNEG:
278                case I2L:
279                case F2L:
280                case D2L:
281                    return StrictBasicValue.LONG_VALUE;
282                case DNEG:
283                case I2D:
284                case L2D:
285                case F2D:
286                    return StrictBasicValue.DOUBLE_VALUE;
287                case IFEQ:
288                case IFNE:
289                case IFLT:
290                case IFGE:
291                case IFGT:
292                case IFLE:
293                case TABLESWITCH:
294                case LOOKUPSWITCH:
295                case IRETURN:
296                case LRETURN:
297                case FRETURN:
298                case DRETURN:
299                case ARETURN:
300                case PUTSTATIC:
301                    return null;
302                case GETFIELD:
303                    return newValue(Type.getType(((FieldInsnNode) insn).desc));
304                case NEWARRAY:
305                    switch (((IntInsnNode) insn).operand) {
306                        case T_BOOLEAN:
307                            return newValue(Type.getType("[Z"));
308                        case T_CHAR:
309                            return newValue(Type.getType("[C"));
310                        case T_BYTE:
311                            return newValue(Type.getType("[B"));
312                        case T_SHORT:
313                            return newValue(Type.getType("[S"));
314                        case T_INT:
315                            return newValue(Type.getType("[I"));
316                        case T_FLOAT:
317                            return newValue(Type.getType("[F"));
318                        case T_DOUBLE:
319                            return newValue(Type.getType("[D"));
320                        case T_LONG:
321                            return newValue(Type.getType("[J"));
322                        default:
323                            throw new AnalyzerException(insn, "Invalid array type");
324                    }
325                case ANEWARRAY:
326                    String desc = ((TypeInsnNode) insn).desc;
327                    return newValue(Type.getType("[" + Type.getObjectType(desc)));
328                case ARRAYLENGTH:
329                    return StrictBasicValue.INT_VALUE;
330                case ATHROW:
331                    return null;
332                case CHECKCAST:
333                    desc = ((TypeInsnNode) insn).desc;
334                    return newValue(Type.getObjectType(desc));
335                case INSTANCEOF:
336                    return StrictBasicValue.INT_VALUE;
337                case MONITORENTER:
338                case MONITOREXIT:
339                case IFNULL:
340                case IFNONNULL:
341                    return null;
342                default:
343                    throw new Error("Internal error.");
344            }
345        }
346    
347        @NotNull
348        @Override
349        public BasicValue merge(
350                @NotNull BasicValue v, @NotNull BasicValue w
351        ) {
352            if (v.equals(w)) return v;
353    
354            if (tolerantToUninitializedValues) {
355                if (v == StrictBasicValue.UNINITIALIZED_VALUE) return w;
356                if (w == StrictBasicValue.UNINITIALIZED_VALUE) return v;
357            }
358            else if (v == StrictBasicValue.UNINITIALIZED_VALUE || w == StrictBasicValue.UNINITIALIZED_VALUE) {
359                return StrictBasicValue.UNINITIALIZED_VALUE;
360            }
361    
362            // if merge of two references then `lub` is java/lang/Object
363            // arrays also are BasicValues with reference type's
364            if (v.getType().getSort() == Type.OBJECT && w.getType().getSort() == Type.OBJECT) {
365                return StrictBasicValue.REFERENCE_VALUE;
366            }
367    
368            assert v.getType().getSort() != Type.ARRAY && w.getType().getSort() != Type.ARRAY : "There should not be arrays";
369    
370    
371            // if merge of something can be stored in int var (int, char, boolean, byte, character)
372            if (v.getType().getOpcode(Opcodes.ISTORE) == Opcodes.ISTORE &&
373                w.getType().getOpcode(Opcodes.ISTORE) == Opcodes.ISTORE) {
374                return StrictBasicValue.INT_VALUE;
375            }
376    
377            return StrictBasicValue.UNINITIALIZED_VALUE;
378        }
379    }