001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.nabla.forward.analysis;
018    
019    import java.util.Iterator;
020    import java.util.List;
021    
022    import org.apache.commons.nabla.DifferentiationException;
023    import org.apache.commons.nabla.NablaMessages;
024    import org.objectweb.asm.Handle;
025    import org.objectweb.asm.Opcodes;
026    import org.objectweb.asm.Type;
027    import org.objectweb.asm.tree.AbstractInsnNode;
028    import org.objectweb.asm.tree.FieldInsnNode;
029    import org.objectweb.asm.tree.IntInsnNode;
030    import org.objectweb.asm.tree.InvokeDynamicInsnNode;
031    import org.objectweb.asm.tree.LdcInsnNode;
032    import org.objectweb.asm.tree.MethodInsnNode;
033    import org.objectweb.asm.tree.MultiANewArrayInsnNode;
034    import org.objectweb.asm.tree.TypeInsnNode;
035    import org.objectweb.asm.tree.analysis.AnalyzerException;
036    import org.objectweb.asm.tree.analysis.Interpreter;
037    
038    /** An interpreter tracking which instructions use which values.
039     * <p>
040     * The implementation of this class is largely copied from the original BasicInterpreter from asm,
041     * which is distributed under the following terms:
042     * </p>
043     * <p>
044     * ASM: a very small and fast Java bytecode manipulation framework
045     * Copyright (c) 2000-2011 INRIA, France Telecom
046     * All rights reserved.
047     *
048     * Redistribution and use in source and binary forms, with or without
049     * modification, are permitted provided that the following conditions
050     * are met:
051     * 1. Redistributions of source code must retain the above copyright
052     *    notice, this list of conditions and the following disclaimer.
053     * 2. Redistributions in binary form must reproduce the above copyright
054     *    notice, this list of conditions and the following disclaimer in the
055     *    documentation and/or other materials provided with the distribution.
056     * 3. Neither the name of the copyright holders nor the names of its
057     *    contributors may be used to endorse or promote products derived from
058     *    this software without specific prior written permission.
059     *
060     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
061     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
062     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
063     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
064     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
065     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
066     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
067     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
068     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
069     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
070     * THE POSSIBILITY OF SUCH DAMAGE.
071     * </p>
072     * @see TrackingValue
073     * @version $Id$
074     */
075    public class TrackingInterpreter extends Interpreter<TrackingValue> {
076    
077        /** Build an interpreter.
078         */
079        public TrackingInterpreter() {
080            super(Opcodes.ASM4);
081        }
082    
083        /** {@inheritDoc} */
084        @Override
085        public TrackingValue newValue(final Type type) {
086            return (type == null) ? TrackingValue.UNINITIALIZED_VALUE : new TrackingValue(type);
087        }
088    
089        /** {@inheritDoc} */
090        @Override
091        public TrackingValue newOperation(final AbstractInsnNode insn) throws AnalyzerException {
092            switch (insn.getOpcode()) {
093                case Opcodes.ACONST_NULL:
094                    return new TrackingValue(Type.getObjectType("null"), insn);
095                case Opcodes.ICONST_M1:
096                case Opcodes.ICONST_0:
097                case Opcodes.ICONST_1:
098                case Opcodes.ICONST_2:
099                case Opcodes.ICONST_3:
100                case Opcodes.ICONST_4:
101                case Opcodes.ICONST_5:
102                    return new TrackingValue(Type.INT_TYPE, insn);
103                case Opcodes.LCONST_0:
104                case Opcodes.LCONST_1:
105                    return new TrackingValue(Type.LONG_TYPE, insn);
106                case Opcodes.FCONST_0:
107                case Opcodes.FCONST_1:
108                case Opcodes.FCONST_2:
109                    return new TrackingValue(Type.FLOAT_TYPE, insn);
110                case Opcodes.DCONST_0:
111                case Opcodes.DCONST_1:
112                    return new TrackingValue(Type.DOUBLE_TYPE, insn);
113                case Opcodes.BIPUSH:
114                case Opcodes.SIPUSH:
115                    return new TrackingValue(Type.INT_TYPE, insn);
116                case Opcodes.LDC:
117                    final Object cst = ((LdcInsnNode) insn).cst;
118                    if (cst instanceof Integer) {
119                        return new TrackingValue(Type.INT_TYPE, insn);
120                    } else if (cst instanceof Float) {
121                        return new TrackingValue(Type.FLOAT_TYPE, insn);
122                    } else if (cst instanceof Long) {
123                        return new TrackingValue(Type.LONG_TYPE, insn);
124                    } else if (cst instanceof Double) {
125                        return new TrackingValue(Type.DOUBLE_TYPE, insn);
126                    } else if (cst instanceof String) {
127                        return new TrackingValue(Type.getType(String.class), insn);
128                    } else if (cst instanceof Type) {
129                        final int sort = ((Type) cst).getSort();
130                        if (sort == Type.OBJECT || sort == Type.ARRAY) {
131                            return new TrackingValue(Type.getType(Class.class), insn);
132                        } else if (sort == Type.METHOD) {
133                            return new TrackingValue(Type.getObjectType("java/lang/invoke/MethodType"), insn);
134                        } else {
135                            throw DifferentiationException.createIllegalArgumentException(NablaMessages.ILLEGAL_LDC_CONSTANT,
136                                                                                          cst);
137                        }
138                    } else if (cst instanceof Handle) {
139                        return new TrackingValue(Type.getObjectType("java/lang/invoke/MethodHandle"), insn);
140                    } else {
141                        throw DifferentiationException.createIllegalArgumentException(NablaMessages.ILLEGAL_LDC_CONSTANT,
142                                                                                      cst);
143                    }
144                case Opcodes.JSR:
145                    return new TrackingValue(Type.VOID_TYPE, insn); // return address value
146                case Opcodes.GETSTATIC:
147                    return new TrackingValue(Type.getType(((FieldInsnNode) insn).desc), insn);
148                case Opcodes.NEW:
149                    return new TrackingValue(Type.getObjectType(((TypeInsnNode) insn).desc), insn);
150                default:
151                    throw DifferentiationException.createInternalError(null);
152            }
153        }
154    
155        /** {@inheritDoc} */
156        public TrackingValue unaryOperation(final AbstractInsnNode insn, final TrackingValue value)
157            throws AnalyzerException {
158            ((TrackingValue) value).addConsumer(insn);
159            switch (insn.getOpcode()) {
160                case Opcodes.INEG:
161                case Opcodes.IINC:
162                case Opcodes.L2I:
163                case Opcodes.F2I:
164                case Opcodes.D2I:
165                case Opcodes.I2B:
166                case Opcodes.I2C:
167                case Opcodes.I2S:
168                    return new TrackingValue(Type.INT_TYPE, insn);
169                case Opcodes.FNEG:
170                case Opcodes.I2F:
171                case Opcodes.L2F:
172                case Opcodes.D2F:
173                    return new TrackingValue(Type.FLOAT_TYPE, insn);
174                case Opcodes.LNEG:
175                case Opcodes.I2L:
176                case Opcodes.F2L:
177                case Opcodes.D2L:
178                    return new TrackingValue(Type.LONG_TYPE, insn);
179                case Opcodes.DNEG:
180                case Opcodes.I2D:
181                case Opcodes.L2D:
182                case Opcodes.F2D:
183                    return new TrackingValue(Type.DOUBLE_TYPE, insn);
184                case Opcodes.IFEQ:
185                case Opcodes.IFNE:
186                case Opcodes.IFLT:
187                case Opcodes.IFGE:
188                case Opcodes.IFGT:
189                case Opcodes.IFLE:
190                case Opcodes.TABLESWITCH:
191                case Opcodes.LOOKUPSWITCH:
192                case Opcodes.IRETURN:
193                case Opcodes.LRETURN:
194                case Opcodes.FRETURN:
195                case Opcodes.DRETURN:
196                case Opcodes.ARETURN:
197                case Opcodes.PUTSTATIC:
198                    return null;
199                case Opcodes.GETFIELD:
200                    return new TrackingValue(Type.getType(((FieldInsnNode) insn).desc), insn);
201                case Opcodes.NEWARRAY:
202                    switch (((IntInsnNode) insn).operand) {
203                        case Opcodes.T_BOOLEAN:
204                            return new TrackingValue(Type.getType("[Z"), insn);
205                        case Opcodes.T_CHAR:
206                            return new TrackingValue(Type.getType("[C"), insn);
207                        case Opcodes.T_BYTE:
208                            return new TrackingValue(Type.getType("[B"), insn);
209                        case Opcodes.T_SHORT:
210                            return new TrackingValue(Type.getType("[S"), insn);
211                        case Opcodes.T_INT:
212                            return new TrackingValue(Type.getType("[I"), insn);
213                        case Opcodes.T_FLOAT:
214                            return new TrackingValue(Type.getType("[F"), insn);
215                        case Opcodes.T_DOUBLE:
216                            return new TrackingValue(Type.getType("[D"), insn);
217                        case Opcodes.T_LONG:
218                            return new TrackingValue(Type.getType("[J"), insn);
219                        default:
220                            throw new AnalyzerException(insn, "Invalid array type");
221                    }
222                case Opcodes.ANEWARRAY: {
223                    final String desc = ((TypeInsnNode) insn).desc;
224                    return new TrackingValue(Type.getType("[" + Type.getObjectType(desc)), insn);
225                }
226                case Opcodes.ARRAYLENGTH:
227                    return new TrackingValue(Type.INT_TYPE, insn);
228                case Opcodes.ATHROW:
229                    return null;
230                case Opcodes.CHECKCAST: {
231                    final String desc = ((TypeInsnNode) insn).desc;
232                    return new TrackingValue(Type.getObjectType(desc), insn);
233                }
234                case Opcodes.INSTANCEOF:
235                    return new TrackingValue(Type.INT_TYPE, insn);
236                case Opcodes.MONITORENTER:
237                case Opcodes.MONITOREXIT:
238                case Opcodes.IFNULL:
239                case Opcodes.IFNONNULL:
240                    return null;
241                default:
242                    throw DifferentiationException.createInternalError(null);
243            }
244        }
245    
246        /** {@inheritDoc} */
247        public TrackingValue binaryOperation(final AbstractInsnNode insn,
248                                             final TrackingValue value1, final TrackingValue value2)
249            throws AnalyzerException {
250            ((TrackingValue) value1).addConsumer(insn);
251            ((TrackingValue) value2).addConsumer(insn);
252            switch (insn.getOpcode()) {
253                case Opcodes.IALOAD:
254                case Opcodes.BALOAD:
255                case Opcodes.CALOAD:
256                case Opcodes.SALOAD:
257                case Opcodes.IADD:
258                case Opcodes.ISUB:
259                case Opcodes.IMUL:
260                case Opcodes.IDIV:
261                case Opcodes.IREM:
262                case Opcodes.ISHL:
263                case Opcodes.ISHR:
264                case Opcodes.IUSHR:
265                case Opcodes.IAND:
266                case Opcodes.IOR:
267                case Opcodes.IXOR:
268                    return new TrackingValue(Type.INT_TYPE, insn);
269                case Opcodes.FALOAD:
270                case Opcodes.FADD:
271                case Opcodes.FSUB:
272                case Opcodes.FMUL:
273                case Opcodes.FDIV:
274                case Opcodes.FREM:
275                    return new TrackingValue(Type.FLOAT_TYPE, insn);
276                case Opcodes.LALOAD:
277                case Opcodes.LADD:
278                case Opcodes.LSUB:
279                case Opcodes.LMUL:
280                case Opcodes.LDIV:
281                case Opcodes.LREM:
282                case Opcodes.LSHL:
283                case Opcodes.LSHR:
284                case Opcodes.LUSHR:
285                case Opcodes.LAND:
286                case Opcodes.LOR:
287                case Opcodes.LXOR:
288                    return new TrackingValue(Type.LONG_TYPE, insn);
289                case Opcodes.DALOAD:
290                case Opcodes.DADD:
291                case Opcodes.DSUB:
292                case Opcodes.DMUL:
293                case Opcodes.DDIV:
294                case Opcodes.DREM:
295                    return new TrackingValue(Type.DOUBLE_TYPE, insn);
296                case Opcodes.AALOAD:
297                    return new TrackingValue(Type.getType(Object.class), insn); // reference value
298                case Opcodes.LCMP:
299                case Opcodes.FCMPL:
300                case Opcodes.FCMPG:
301                case Opcodes.DCMPL:
302                case Opcodes.DCMPG:
303                    return new TrackingValue(Type.INT_TYPE, insn);
304                case Opcodes.IF_ICMPEQ:
305                case Opcodes.IF_ICMPNE:
306                case Opcodes.IF_ICMPLT:
307                case Opcodes.IF_ICMPGE:
308                case Opcodes.IF_ICMPGT:
309                case Opcodes.IF_ICMPLE:
310                case Opcodes.IF_ACMPEQ:
311                case Opcodes.IF_ACMPNE:
312                case Opcodes.PUTFIELD:
313                    return null;
314                default:
315                    throw DifferentiationException.createInternalError(null);
316            }
317        }
318    
319        /** {@inheritDoc} */
320        public TrackingValue ternaryOperation(final AbstractInsnNode insn,
321                                              final TrackingValue value1, final TrackingValue value2,
322                                              final TrackingValue value3)
323            throws AnalyzerException {
324            value1.addConsumer(insn);
325            value2.addConsumer(insn);
326            value3.addConsumer(insn);
327            return new TrackingValue(null, insn);
328        }
329    
330        /** {@inheritDoc} */
331        public TrackingValue naryOperation(final AbstractInsnNode insn, final List<? extends TrackingValue> values)
332            throws AnalyzerException {
333            for (final Iterator<?> iterator = values.iterator(); iterator.hasNext();) {
334                ((TrackingValue) iterator.next()).addConsumer(insn);
335            }
336            final int opcode = insn.getOpcode();
337            if (opcode == Opcodes.MULTIANEWARRAY) {
338                return new TrackingValue(Type.getType(((MultiANewArrayInsnNode) insn).desc), insn);
339            } else if (opcode == Opcodes.INVOKEDYNAMIC) {
340                return new TrackingValue(Type.getReturnType(((InvokeDynamicInsnNode) insn).desc), insn);
341            } else {
342                return new TrackingValue(Type.getReturnType(((MethodInsnNode) insn).desc), insn);
343            }
344        }
345    
346        /** {@inheritDoc} */
347        public TrackingValue copyOperation(final AbstractInsnNode insn, final TrackingValue value)
348            throws AnalyzerException {
349            // we reuse the same instance instead of wrapping it again
350            // thus simplifying transitive dependencies propagation
351            value.addConsumer(insn);
352            value.addProducer(insn);
353            return value;
354        }
355    
356        /** {@inheritDoc} */
357        public TrackingValue merge(final TrackingValue v, final TrackingValue w) {
358            TrackingValue.merge(v, w);
359            return v;
360        }
361    
362        /** {@inheritDoc} */
363        public void returnOperation(final AbstractInsnNode insn,
364                                    final TrackingValue value, final TrackingValue expected) {
365            // nothing to do here, as unaryOperation has already been called
366        }
367    
368    }