Coverage Report - org.apache.commons.nabla.forward.analysis.MethodDifferentiator
 
Classes in this File Line Coverage Branch Coverage Complexity
MethodDifferentiator
77%
170/219
66%
101/153
8.941
MethodDifferentiator$FlowAnalyzer
100%
11/11
100%
2/2
8.941
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.apache.commons.nabla.forward.analysis;
 18  
 
 19  
 import java.util.ArrayList;
 20  
 import java.util.HashSet;
 21  
 import java.util.IdentityHashMap;
 22  
 import java.util.Iterator;
 23  
 import java.util.List;
 24  
 import java.util.ListIterator;
 25  
 import java.util.Map;
 26  
 import java.util.Set;
 27  
 
 28  
 import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
 29  
 import org.apache.commons.nabla.DifferentiationException;
 30  
 import org.apache.commons.nabla.NablaMessages;
 31  
 import org.apache.commons.nabla.forward.arithmetic.DAddTransformer;
 32  
 import org.apache.commons.nabla.forward.arithmetic.DDivTransformer;
 33  
 import org.apache.commons.nabla.forward.arithmetic.DMulTransformer;
 34  
 import org.apache.commons.nabla.forward.arithmetic.DNegTransformer;
 35  
 import org.apache.commons.nabla.forward.arithmetic.DRemTransformer;
 36  
 import org.apache.commons.nabla.forward.arithmetic.DSubTransformer;
 37  
 import org.apache.commons.nabla.forward.instructions.DLoadTransformer;
 38  
 import org.apache.commons.nabla.forward.instructions.DReturnTransformer;
 39  
 import org.apache.commons.nabla.forward.instructions.DStoreTransformer;
 40  
 import org.apache.commons.nabla.forward.instructions.DcmpTransformer;
 41  
 import org.apache.commons.nabla.forward.instructions.Dup2Transformer;
 42  
 import org.apache.commons.nabla.forward.instructions.Dup2X1Transformer;
 43  
 import org.apache.commons.nabla.forward.instructions.Dup2X2Transformer;
 44  
 import org.apache.commons.nabla.forward.instructions.InvokeStaticTransformer;
 45  
 import org.apache.commons.nabla.forward.instructions.NarrowingTransformer;
 46  
 import org.apache.commons.nabla.forward.instructions.Pop2Transformer;
 47  
 import org.apache.commons.nabla.forward.instructions.WideningTransformer;
 48  
 import org.apache.commons.nabla.forward.trimming.DLoadPop2Trimmer;
 49  
 import org.apache.commons.nabla.forward.trimming.SwappedDloadTrimmer;
 50  
 import org.apache.commons.nabla.forward.trimming.SwappedDstoreTrimmer;
 51  
 import org.objectweb.asm.Opcodes;
 52  
 import org.objectweb.asm.Type;
 53  
 import org.objectweb.asm.tree.AbstractInsnNode;
 54  
 import org.objectweb.asm.tree.FieldInsnNode;
 55  
 import org.objectweb.asm.tree.IincInsnNode;
 56  
 import org.objectweb.asm.tree.InsnList;
 57  
 import org.objectweb.asm.tree.InsnNode;
 58  
 import org.objectweb.asm.tree.LdcInsnNode;
 59  
 import org.objectweb.asm.tree.MethodInsnNode;
 60  
 import org.objectweb.asm.tree.MethodNode;
 61  
 import org.objectweb.asm.tree.TypeInsnNode;
 62  
 import org.objectweb.asm.tree.VarInsnNode;
 63  
 import org.objectweb.asm.tree.analysis.Analyzer;
 64  
 import org.objectweb.asm.tree.analysis.AnalyzerException;
 65  
 import org.objectweb.asm.tree.analysis.Frame;
 66  
 import org.objectweb.asm.tree.analysis.Interpreter;
 67  
 
 68  
 /** Class transforming a method computing a value to a method
 69  
  * computing both a value and its differential.
 70  
  * @version $Id$
 71  
  */
 72  448
 public class MethodDifferentiator {
 73  
 
 74  
     /** Maximal number of temporary size 2 variables. */
 75  
     private static final int MAX_TEMP = 5;
 76  
 
 77  
     /** Math implementation classes. */
 78  
     private final Set<String> mathClasses;
 79  
 
 80  
     /** Name of the derived class. */
 81  
     private final String derivedName;
 82  
 
 83  
     /** Used locals variables array. */
 84  
     private boolean[] usedLocals;
 85  
 
 86  
     /** Set of converted values. */
 87  
     private final Set<TrackingValue> converted;
 88  
 
 89  
     /** Frames for the original method. */
 90  
     private final Map<AbstractInsnNode, Frame<TrackingValue>> frames;
 91  
 
 92  
     /** Instructions successors array. */
 93  
     private final Map<AbstractInsnNode, Set<AbstractInsnNode>> successors;
 94  
 
 95  
     /** Build a differentiator for a method.
 96  
      * @param mathClasses math implementation classes
 97  
      * @param derivedName name of the derived class
 98  
      */
 99  67
     public MethodDifferentiator(final Set<String> mathClasses, final String derivedName) {
 100  67
         this.usedLocals   = null;
 101  67
         this.mathClasses  = mathClasses;
 102  67
         this.derivedName  = derivedName;
 103  67
         this.converted    = new HashSet<TrackingValue>();
 104  67
         this.frames       = new IdentityHashMap<AbstractInsnNode, Frame<TrackingValue>>();
 105  67
         this.successors   = new IdentityHashMap<AbstractInsnNode, Set<AbstractInsnNode>>();
 106  67
     }
 107  
 
 108  
     /** Get the index of the input {@link DerivativeStructure derivative structure} variable.
 109  
      * @return index of the input {@link DerivativeStructure derivative structure} variable
 110  
      */
 111  
     public int getInputDSIndex() {
 112  
         // TODO: this should be improved in the general case,
 113  
         // as we are not sure the variable will always remain at index 1
 114  8
         return 1;
 115  
     }
 116  
 
 117  
     /**
 118  
      * Differentiate a method.
 119  
      * @param primitiveName primitive class name
 120  
      * @param method method to differentiate (<em>will</em> be modified)
 121  
      * @exception DifferentiationException if method cannot be differentiated
 122  
      */
 123  
     public void differentiate(final String primitiveName, final MethodNode method)
 124  
         throws DifferentiationException {
 125  
         try {
 126  
 
 127  
             // at start, "this" and one DerivativeStructure are already used
 128  67
             method.maxLocals  = 2 * (method.maxLocals + MAX_TEMP) - 1;
 129  67
             usedLocals = new boolean[method.maxLocals];
 130  67
             useLocal(0, 1);
 131  67
             useLocal(1, 4);
 132  
 
 133  67
             final Type dsType = Type.getType(DerivativeStructure.class);
 134  
 
 135  
             // add spare cells to hold new variables if needed
 136  67
             addSpareLocalVariables(method.instructions);
 137  
 
 138  
             // analyze the original code, tracing values production/consumption
 139  67
             final FlowAnalyzer analyzer =
 140  
                 new FlowAnalyzer(new TrackingInterpreter(), method.instructions);
 141  67
             final Frame<TrackingValue>[] array = analyzer.analyze(primitiveName, method);
 142  
 
 143  
             // convert the array into a map, since code changes will shift all indices
 144  357
             for (int i = 0; i < array.length; ++i) {
 145  290
                 frames.put(method.instructions.get(i), array[i]);
 146  
             }
 147  
 
 148  
             // identify the needed changes
 149  67
             final Set<AbstractInsnNode> changes = identifyChanges(method.instructions);
 150  
 
 151  67
             if (changes.isEmpty()) {
 152  
 
 153  
                 // TODO: merge this case with the general case: DRETURN must always be
 154  
                 // changed when the function signature changes, and the change is either
 155  
                 // converting DRETURN to ARETURN, possibly with a prepended call to
 156  
                 // convertDoubleToDerivativeStructure before the ARETURN when stack0converted
 157  
                 // is false
 158  
 
 159  
                 // the method does not depend on the parameter at all!
 160  
                 // we replace all "return d;" by "return new DerivativeStructure(parameters, order, d);"
 161  1
                 for (final Iterator<AbstractInsnNode> i = method.instructions.iterator(); i.hasNext();) {
 162  2
                     final AbstractInsnNode insn = i.next();
 163  2
                     if (insn.getOpcode() == Opcodes.DRETURN) {
 164  1
                         final InsnList list = new DReturnTransformer(false).getReplacement(insn, this);
 165  1
                         method.instructions.insert(insn, list);
 166  1
                         method.instructions.remove(insn);
 167  
                     }
 168  2
                 }
 169  
 
 170  
             } else {
 171  
 
 172  
                 // perform the code changes
 173  66
                 for (final AbstractInsnNode insn : changes) {
 174  243
                     method.instructions.insert(insn, getReplacement(insn));
 175  243
                     method.instructions.remove(insn);
 176  
                 }
 177  
 
 178  
                 // trim generated instructions list
 179  66
                 new SwappedDloadTrimmer().trim(method.instructions);
 180  66
                 new SwappedDstoreTrimmer().trim(method.instructions);
 181  66
                 new DLoadPop2Trimmer().trim(method.instructions);
 182  
 
 183  
             }
 184  
 
 185  
             // remove the local variables added at the beginning and not used
 186  67
             removeUnusedSpareLocalVariables(method.instructions);
 187  
 
 188  
             // set the method properties
 189  67
             method.desc       = Type.getMethodDescriptor(dsType, dsType);
 190  67
             method.access    |= Opcodes.ACC_SYNTHETIC;
 191  67
             method.maxLocals  = maxVariables();
 192  
 
 193  0
         } catch (AnalyzerException ae) {
 194  0
             ae.printStackTrace(System.err);
 195  0
             if ((ae.getCause() != null) && ae.getCause() instanceof DifferentiationException) {
 196  0
                 throw (DifferentiationException) ae.getCause();
 197  
             } else {
 198  0
                 throw new DifferentiationException(NablaMessages.UNABLE_TO_ANALYZE_METHOD,
 199  
                                                    primitiveName, method.name, ae.getMessage());
 200  
             }
 201  67
         }
 202  67
     }
 203  
 
 204  
     /** Add spare cells for new local variables.
 205  
      * <p>In order to ease conversion from double values to derivative structures,
 206  
      * we start by reserving one spare cell between each original local variables.
 207  
      * So we have to modify the indices in all instructions referencing local
 208  
      * variables in the original code, to take into account the renumbering
 209  
      * introduced by these spare cells. The spare cells by themselves will
 210  
      * be referenced by the converted instructions in the following passes.</p>
 211  
      * <p>The spare cells that will not be used will be reclaimed after
 212  
      * conversion, to avoid wasting memory.</p>
 213  
      * @param instructions instructions of the method
 214  
      * @exception DifferentiationException if local variables array has not been
 215  
      * expanded appropriately beforehand
 216  
      * @see #removeUnusedSpareLocalVariables()
 217  
      */
 218  
     private void addSpareLocalVariables(final InsnList instructions)
 219  
         throws DifferentiationException {
 220  67
         for (final Iterator<AbstractInsnNode> i = instructions.iterator(); i.hasNext();) {
 221  290
             final AbstractInsnNode insn = i.next();
 222  290
             if (insn.getType() == AbstractInsnNode.VAR_INSN) {
 223  99
                 final VarInsnNode varInsn = (VarInsnNode) insn;
 224  99
                 if (varInsn.var > 2) {
 225  15
                     varInsn.var = 2 * varInsn.var - 1;
 226  15
                     final int opcode = varInsn.getOpcode();
 227  15
                     if ((opcode == Opcodes.ILOAD)  || (opcode == Opcodes.FLOAD)  ||
 228  
                         (opcode == Opcodes.ALOAD)  || (opcode == Opcodes.ISTORE) ||
 229  
                         (opcode == Opcodes.FSTORE) || (opcode == Opcodes.ASTORE)) {
 230  4
                         useLocal(varInsn.var, 1);
 231  
                     } else {
 232  11
                         useLocal(varInsn.var, 2);
 233  
                     }
 234  
                 }
 235  99
             } else if (insn.getOpcode() == Opcodes.IINC) {
 236  2
                 final IincInsnNode iincInsn = (IincInsnNode) insn;
 237  2
                 if (iincInsn.var > 2) {
 238  2
                     iincInsn.var = 2 * iincInsn.var - 1;
 239  2
                     useLocal(iincInsn.var, 1);
 240  
                 }
 241  
             }
 242  290
         }
 243  67
     }
 244  
 
 245  
     /** Remove the unused spare cells introduced at conversion start.
 246  
      * @param instructions instructions of the method
 247  
      * @see #addSpareLocalVariables()
 248  
      */
 249  
     private void removeUnusedSpareLocalVariables(final InsnList instructions) {
 250  67
         for (final Iterator<AbstractInsnNode> i = instructions.iterator(); i.hasNext();) {
 251  428
             final AbstractInsnNode insn = i.next();
 252  428
             if (insn.getType() == AbstractInsnNode.VAR_INSN) {
 253  119
                 shiftVariable((VarInsnNode) insn);
 254  
             }
 255  428
         }
 256  67
     }
 257  
 
 258  
     /** Identify the instructions that must be changed.
 259  
      * <p>Identification is based on data flow analysis. We start by changing
 260  
      * the local variables in the initial frame to match the parameters of
 261  
      * the derivative method, and propagate these variables following the
 262  
      * instructions path, updating stack cells and local variables as needed.
 263  
      * Instructions that must be changed are the ones that consume changed
 264  
      * variables or stack cells.</p>
 265  
      * @param instructions instructions of the method
 266  
      * @return set containing all the instructions that must be changed
 267  
      */
 268  
     private Set<AbstractInsnNode> identifyChanges(final InsnList instructions) {
 269  
 
 270  
         // the pending set contains the values (local variables or stack cells)
 271  
         // that have been changed, they will trigger changes on the instructions
 272  
         // that consume them
 273  67
         final Set<TrackingValue> pending = new HashSet<TrackingValue>();
 274  
 
 275  
         // the changes set contains the instructions that must be changed
 276  67
         final Set<AbstractInsnNode> changes = new HashSet<AbstractInsnNode>();
 277  
 
 278  
         // start by converting the parameter of the method,
 279  
         // which is kept in local variable 1 of the initial frame
 280  67
         final TrackingValue dpParameter = frames.get(instructions.get(0)).getLocal(1);
 281  67
         pending.add(dpParameter);
 282  
 
 283  
         // propagate the values conversions throughout the method
 284  215
         while (!pending.isEmpty()) {
 285  
 
 286  
             // pop one element from the set of changed values
 287  148
             final Iterator<TrackingValue> iterator = pending.iterator();
 288  148
             final TrackingValue value = iterator.next();
 289  148
             iterator.remove();
 290  
 
 291  
             // this value is converted
 292  148
             converted.add(value);
 293  
 
 294  
             // check the consumers instructions for this value
 295  148
             for (final AbstractInsnNode consumer : value.getConsumers()) {
 296  
 
 297  
                 // an instruction consuming a converted value and producing
 298  
                 // a double must be changed to produce a derivative structure,
 299  
                 // get the double values produced and add them to the changed set
 300  260
                 pending.addAll(getProducedAndNotConvertedDoubleValues(consumer));
 301  
 
 302  
                 // as a consumer of a converted value, the instruction must be changed
 303  260
                 changes.add(consumer);
 304  
 
 305  
             }
 306  
 
 307  
             // check the producers instructions for this value
 308  148
             for (final AbstractInsnNode producer : value.getProducers()) {
 309  
 
 310  
                 // an instruction producing a converted value must be changed
 311  187
                 changes.add(producer);
 312  
 
 313  
             }
 314  148
         }
 315  
 
 316  
         // the various GETFIELD/PUTFIELD instructions must also be changed
 317  
         // to retrieve the field from the outer class
 318  67
         final ListIterator<AbstractInsnNode> iterator = instructions.iterator();
 319  357
         while (iterator.hasNext()) {
 320  290
             final AbstractInsnNode ins = iterator.next();
 321  290
             if ((ins.getOpcode() == Opcodes.GETFIELD) || (ins.getOpcode() == Opcodes.PUTFIELD)) {
 322  1
                 changes.add(ins);
 323  
             }
 324  290
         }
 325  
 
 326  67
         return changes;
 327  
 
 328  
     }
 329  
 
 330  
     /** Get the list of double values produced by an instruction and not yet converted.
 331  
      * @param instruction instruction producing the values
 332  
      * @return list of double values produced
 333  
      */
 334  
     private List<TrackingValue> getProducedAndNotConvertedDoubleValues(final AbstractInsnNode instruction) {
 335  
 
 336  260
         final List<TrackingValue> values = new ArrayList<TrackingValue>();
 337  
 
 338  
         // get the frame before instruction execution
 339  260
         final Frame<TrackingValue> before = frames.get(instruction);
 340  260
         final int beforeStackSize = before.getStackSize();
 341  260
         final int locals = before.getLocals();
 342  
 
 343  
         // check the frames produced by this instruction
 344  
         // (they correspond to the input frames of its successors)
 345  260
         final Set<AbstractInsnNode> set = successors.get(instruction);
 346  260
         if (set != null) {
 347  
 
 348  
             // loop over the successors of this instruction
 349  192
             for (final AbstractInsnNode successor : set) {
 350  192
                 final Frame<TrackingValue> produced = frames.get(successor);
 351  
 
 352  
                 // check the stack cells
 353  412
                 for (int i = 0; i < produced.getStackSize(); ++i) {
 354  220
                     final TrackingValue value = produced.getStack(i);
 355  220
                     if (((i >= beforeStackSize) || (value != before.getStack(i))) &&
 356  
                         value.getType().equals(Type.DOUBLE_TYPE) &&
 357  
                         !converted.contains(value)) {
 358  86
                         values.add(value);
 359  
                     }
 360  
                 }
 361  
 
 362  
                 // check the local variables
 363  3240
                 for (int i = 0; i < locals; ++i) {
 364  3048
                     final TrackingValue value = (TrackingValue) produced.getLocal(i);
 365  3048
                     if ((value != before.getLocal(i)) &&
 366  
                         value.getType().equals(Type.DOUBLE_TYPE) &&
 367  
                         !converted.contains(value)) {
 368  2
                         values.add(value);
 369  
                     }
 370  
                 }
 371  192
             }
 372  
         }
 373  
 
 374  260
         return values;
 375  
 
 376  
     }
 377  
 
 378  
     /** Get the replacement list for an instruction.
 379  
      * @param insn instruction to replace
 380  
      * @return replacement instructions list
 381  
      * @exception DifferentiationException if some instruction cannot be handled
 382  
      * or if no temporary variable can be reserved
 383  
      */
 384  
     private InsnList getReplacement(final AbstractInsnNode insn)
 385  
         throws DifferentiationException {
 386  
 
 387  
         // get the frame at the start of the instruction
 388  243
         final Frame<TrackingValue> frame = frames.get(insn);
 389  243
         final int size = frame.getStackSize();
 390  243
         final boolean stack1Converted = (size > 0) && converted.contains(frame.getStack(size - 2));
 391  243
         final boolean stack0Converted = (size > 1) && converted.contains(frame.getStack(size - 1));
 392  
 
 393  243
         switch(insn.getOpcode()) {
 394  
             case Opcodes.DLOAD :
 395  89
                 useLocal(((VarInsnNode) insn).var, 4);
 396  89
                 return new DLoadTransformer().getReplacement(insn, this);
 397  
             case Opcodes.DALOAD :
 398  
                 // TODO: add support for DALOAD differentiation
 399  0
                 throw new RuntimeException("DALOAD not handled yet");
 400  
             case Opcodes.DSTORE :
 401  5
                 useLocal(((VarInsnNode) insn).var, 4);
 402  5
                 return new DStoreTransformer().getReplacement(insn, this);
 403  
             case Opcodes.DASTORE :
 404  
                 // TODO: add support for DASTORE differentiation
 405  0
                 throw new RuntimeException("DASTORE not handled yet");
 406  
             case Opcodes.DUP2 :
 407  0
                 return new Dup2Transformer().getReplacement(insn, this);
 408  
             case Opcodes.POP2 :
 409  0
                 return new Pop2Transformer().getReplacement(insn, this);
 410  
             case Opcodes.DUP2_X1 :
 411  0
                 return new Dup2X1Transformer().getReplacement(insn, this);
 412  
             case Opcodes.DUP2_X2 :
 413  0
                 return new Dup2X2Transformer(stack0Converted, stack1Converted).getReplacement(insn, this);
 414  
             case Opcodes.DADD :
 415  9
                 return new DAddTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
 416  
             case Opcodes.DSUB :
 417  5
                 return new DSubTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
 418  
             case Opcodes.DMUL :
 419  14
                 return new DMulTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
 420  
             case Opcodes.DDIV :
 421  4
                 return new DDivTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
 422  
             case Opcodes.DREM :
 423  3
                 return new DRemTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
 424  
             case Opcodes.DNEG :
 425  1
                 return new DNegTransformer().getReplacement(insn, this);
 426  
             case Opcodes.DCONST_0 :
 427  
             case Opcodes.DCONST_1 :
 428  
             case Opcodes.LDC :
 429  
             case Opcodes.I2D :
 430  
             case Opcodes.L2D :
 431  
             case Opcodes.F2D :
 432  2
                 return new WideningTransformer().getReplacement(insn, this);
 433  
             case Opcodes.D2I :
 434  
             case Opcodes.D2L :
 435  
             case Opcodes.D2F :
 436  1
                 return new NarrowingTransformer().getReplacement(insn, this);
 437  
             case Opcodes.DCMPL :
 438  
             case Opcodes.DCMPG :
 439  0
                 return new DcmpTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
 440  
             case Opcodes.DRETURN :
 441  
                 // TODO: the constructor parameter forced to true seems strange...
 442  66
                 return new DReturnTransformer(true).getReplacement(insn, this);
 443  
             case Opcodes.GETSTATIC :
 444  
                 // TODO: add support for GETSTATIC differentiation
 445  0
                 throw new RuntimeException("GETSTATIC not handled yet");
 446  
             case Opcodes.PUTSTATIC :
 447  
                 // TODO: add support for PUTSTATIC differentiation
 448  0
                 throw new RuntimeException("PUTSTATIC not handled yet");
 449  
             case Opcodes.GETFIELD :
 450  1
                 return replaceGetField((FieldInsnNode) insn);
 451  
             case Opcodes.PUTFIELD :
 452  
                 // TODO: add support for PUTFIELD differentiation
 453  0
                 throw new RuntimeException("PUTFIELD not handled yet");
 454  
             case Opcodes.INVOKEVIRTUAL :
 455  
                 // TODO: add support for INVOKEVIRTUAL differentiation
 456  0
                 throw new RuntimeException("INVOKEVIRTUAL not handled yet");
 457  
             case Opcodes.INVOKESPECIAL :
 458  
                 // TODO: add support for INVOKESPECIAL differentiation
 459  0
                 throw new RuntimeException("INVOKESPECIAL not handled yet");
 460  
             case Opcodes.INVOKESTATIC :
 461  43
                 return new InvokeStaticTransformer(stack0Converted, stack1Converted).getReplacement(insn, this);
 462  
             case Opcodes.INVOKEINTERFACE :
 463  
                 // TODO: add support for INVOKEINTERFACE differentiation
 464  0
                 throw new RuntimeException("INVOKEINTERFACE not handled yet");
 465  
             case Opcodes.INVOKEDYNAMIC :
 466  
                 // TODO: add support for INVOKEDYNAMIC differentiation
 467  0
                 throw new RuntimeException("INVOKEDYNAMIC not handled yet");
 468  
             case Opcodes.NEWARRAY :
 469  
                 // TODO: add support for NEWARRAY differentiation
 470  0
                 throw new RuntimeException("NEWARRAY not handled yet");
 471  
             case Opcodes.ANEWARRAY :
 472  
                 // TODO: add support for ANEWARRAY differentiation
 473  0
                 throw new RuntimeException("ANEWARRAY not handled yet");
 474  
             case Opcodes.MULTIANEWARRAY :
 475  
                 // TODO: add support for MULTIANEWARRAY differentiation
 476  0
                 throw new RuntimeException("MULTIANEWARRAY not handled yet");
 477  
             default:
 478  0
                 throw new DifferentiationException(NablaMessages.UNABLE_TO_HANDLE_INSTRUCTION, insn.getOpcode());
 479  
         }
 480  
 
 481  
     }
 482  
 
 483  
     /** Replace a GETFIELD instruction.
 484  
      * @param fieldInsn field instruction
 485  
      * @return replacement instructions list
 486  
      * @exception DifferentiationException if instruction cannot be replaced
 487  
      */
 488  
     private InsnList replaceGetField(final FieldInsnNode fieldInsn) throws DifferentiationException {
 489  
 
 490  1
         final InsnList list = new InsnList();
 491  
 
 492  
         // get the field as an object
 493  1
         list.add(new LdcInsnNode(fieldInsn.name));
 494  1
         list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, derivedName, "getPrimitiveField",
 495  
                                     Type.getMethodDescriptor(Type.getType(Object.class),
 496  
                                                              Type.getType(String.class))));
 497  
 
 498  
         // convert it to the expected type
 499  1
         final Type type = Type.getType(fieldInsn.desc);
 500  
         final Type boxedType;
 501  
         final String valueMethodName;
 502  1
         switch (type.getSort()) {
 503  
             case Type.VOID:
 504  0
                 throw new DifferentiationException(NablaMessages.CANNOT_GET_VOID_FIELD, fieldInsn.name);
 505  
             case Type.BOOLEAN:
 506  0
                 valueMethodName = "booleanValue";
 507  0
                 boxedType       = Type.getType(Boolean.class);
 508  0
                 break;
 509  
             case Type.CHAR:
 510  0
                 valueMethodName = "charValue";
 511  0
                 boxedType       = Type.getType(Character.class);
 512  0
                 break;
 513  
             case Type.BYTE:
 514  0
                 valueMethodName = "byteValue";
 515  0
                 boxedType       = Type.getType(Byte.class);
 516  0
                 break;
 517  
             case Type.SHORT:
 518  0
                 valueMethodName = "shortValue";
 519  0
                 boxedType       = Type.getType(Short.class);
 520  0
                 break;
 521  
             case Type.INT:
 522  0
                 valueMethodName = "intValue";
 523  0
                 boxedType       = Type.getType(Integer.class);
 524  0
                 break;
 525  
             case Type.FLOAT:
 526  0
                 valueMethodName = "floatValue";
 527  0
                 boxedType       = Type.getType(Float.class);
 528  0
                 break;
 529  
             case Type.LONG:
 530  0
                 valueMethodName = "longValue";
 531  0
                 boxedType       = Type.getType(Long.class);
 532  0
                 break;
 533  
             case Type.DOUBLE:
 534  1
                 valueMethodName = "doubleValue";
 535  1
                 boxedType       = Type.getType(Double.class);
 536  1
                 break;
 537  
             default :
 538  
                 // do nothing for Type.ARRAY and Type.OBJECT
 539  0
                 valueMethodName = null;
 540  0
                 boxedType       = null;
 541  
 
 542  
         }
 543  1
         if (boxedType != null) {
 544  1
             list.add(new TypeInsnNode(Opcodes.CHECKCAST, boxedType.getInternalName()));
 545  
         }
 546  1
         if (valueMethodName != null) {
 547  1
             list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, boxedType.getInternalName(),
 548  
                                         valueMethodName,
 549  
                                         Type.getMethodDescriptor(type, new Type[0])));
 550  
         }
 551  
 
 552  1
         return list;
 553  
 
 554  
     }
 555  
 
 556  
     /** Test if a class is a math implementation class.
 557  
      * @param name name of the class to test
 558  
      * @return true if the named class is a math implementation class
 559  
      */
 560  
     public boolean isMathImplementationClass(final String name) {
 561  43
         return mathClasses.contains(name);
 562  
     }
 563  
 
 564  
     /** Create instructions to convert a double on top of stack to a {@link DerivativeStructure}.
 565  
      * @return list of conversion instructions
 566  
      */
 567  
     public InsnList doubleToDerivativeStructureConversion() {
 568  
 
 569  6
         final InsnList list = new InsnList();
 570  
 
 571  
         // operand stack initial state: d
 572  6
         list.add(new TypeInsnNode(Opcodes.NEW,
 573  
                                   Type.getInternalName(DerivativeStructure.class))); // => d y_ds
 574  6
         list.add(new InsnNode(Opcodes.DUP_X2));                                      // => y_ds d y_ds
 575  6
         list.add(new InsnNode(Opcodes.DUP_X2));                                      // => y_ds y_ds d y_ds
 576  6
         list.add(new InsnNode(Opcodes.POP));                                         // => y_ds y_ds d
 577  6
         list.add(new VarInsnNode(Opcodes.ALOAD, getInputDSIndex()));                 // => y_ds y_ds d x_ds
 578  6
         list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
 579  
                                     Type.getInternalName(DerivativeStructure.class),
 580  
                                     "getFreeParameters",
 581  
                                     Type.getMethodDescriptor(Type.INT_TYPE)));       // => y_ds y_ds d params
 582  6
         list.add(new VarInsnNode(Opcodes.ALOAD, 1));                                 // => y_ds y_ds d params x_ds
 583  6
         list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
 584  
                                     Type.getInternalName(DerivativeStructure.class),
 585  
                                     "getOrder",
 586  
                                     Type.getMethodDescriptor(Type.INT_TYPE)));       // => y_ds y_ds d params order
 587  6
         list.add(new InsnNode(Opcodes.DUP2_X2));                                     // => y_ds y_ds params order d params order
 588  6
         list.add(new InsnNode(Opcodes.POP2));                                        // => y_ds y_ds params order d
 589  6
         list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
 590  
                                     Type.getInternalName(DerivativeStructure.class),
 591  
                                     "<init>",
 592  
                                     Type.getMethodDescriptor(Type.VOID_TYPE,
 593  
                                                              Type.INT_TYPE,
 594  
                                                              Type.INT_TYPE,
 595  
                                                              Type.DOUBLE_TYPE)));    // => y_ds
 596  
 
 597  6
         return list;
 598  
 
 599  
     }
 600  
 
 601  
     /** Set a local variable as used by the modified code.
 602  
      * @param index index of the variable
 603  
      * @param size size of the variable (1 or 2 for standard variables,
 604  
      * 4 for special expanded derivative structures)
 605  
      * @exception DifferentiationException if the number of the
 606  
      * temporary variable lies outside of the allowed range
 607  
      */
 608  
     public void useLocal(final int index, final int size)
 609  
         throws DifferentiationException {
 610  246
         if ((index < 0) || ((index + size) > usedLocals.length)) {
 611  0
             throw new DifferentiationException(NablaMessages.INDEX_OF_LOCAL_VARIABLE_OUT_OF_RANGE,
 612  
                                                size, index, 1, MAX_TEMP);
 613  
         }
 614  987
         for (int i = index; i < index + size; ++i) {
 615  741
             usedLocals[i] = true;
 616  
         }
 617  246
     }
 618  
 
 619  
     /** Get index of a size 2 temporary variable.
 620  
      * <p>Temporary variables can be used for very short duration
 621  
      * storage of size 2 values (i.e one double, or one long or two
 622  
      * integers). These variables are reused in many replacement
 623  
      * instructions sequences, so their content may be overridden
 624  
      * at any time: they should be considered to have a scope
 625  
      * limited to one replacement sequence only. This means that
 626  
      * one should <em>not</em> store a value in a variable in one
 627  
      * replacement sequence and retrieve it later in another
 628  
      * replacement sequence as it may have been overridden in
 629  
      * between.</p>
 630  
      * <p>At most 5 temporary variables may be used.</p>
 631  
      * @param number number of the temporary variable (must be
 632  
      * between 1 and the maximal number of available temporary
 633  
      * variables)
 634  
      * @return index of the variable within the local variables
 635  
      * array
 636  
      * @exception DifferentiationException if the number of the
 637  
      * temporary variable lies outside of the allowed range
 638  
      */
 639  
     public int getTmp(final int number) throws DifferentiationException {
 640  1
         if ((number < 0) || (number > MAX_TEMP)) {
 641  0
             throw new DifferentiationException(NablaMessages.NUMBER_OF_TEMPORARY_VARIABLES_OUT_OF_RANGE,
 642  
                                                number, 1, MAX_TEMP);
 643  
         }
 644  1
         final int index = usedLocals.length - 2 * number;
 645  1
         useLocal(index, 2);
 646  1
         return index;
 647  
     }
 648  
 
 649  
     /** Shifted the index of a variable instruction.
 650  
      * @param insn variable instruction
 651  
      */
 652  
     private void shiftVariable(final VarInsnNode insn) {
 653  119
         int shifted = 0;
 654  361
         for (int i = 0; i < insn.var; ++i) {
 655  242
             if (usedLocals[i]) {
 656  210
                 ++shifted;
 657  
             }
 658  
         }
 659  119
         insn.var = shifted;
 660  119
     }
 661  
 
 662  
     /** Compute the maximal number of used local variables.
 663  
      * @return maximal number of used local variables
 664  
      */
 665  
     private int maxVariables() {
 666  67
         int max = 0;
 667  1088
         for (final boolean isUsed : usedLocals) {
 668  1021
             if (isUsed) {
 669  351
                 ++max;
 670  
             }
 671  
         }
 672  67
         return max;
 673  
     }
 674  
 
 675  
     /** Analyzer preserving instructions successors information. */
 676  
     private class FlowAnalyzer extends Analyzer<TrackingValue> {
 677  
 
 678  
         /** Instructions of the method. */
 679  
         private final InsnList instructions;
 680  
 
 681  
         /** Simple constructor.
 682  
          * @param interpreter associated interpreter
 683  
          * @param instructions instructions of the method
 684  
          */
 685  
         public FlowAnalyzer(final Interpreter<TrackingValue> interpreter,
 686  67
                             final InsnList instructions) {
 687  67
             super(interpreter);
 688  67
             this.instructions = instructions;
 689  67
         }
 690  
 
 691  
         /** Store a new edge.
 692  
          * @param insn current instruction
 693  
          * @param successor successor instruction
 694  
          */
 695  
         protected void newControlFlowEdge(final int insn, final int successor) {
 696  
             // store the successor information
 697  225
             final AbstractInsnNode node = instructions.get(insn);
 698  225
             Set<AbstractInsnNode> set = successors.get(node);
 699  225
             if (set == null) {
 700  223
                 set = new HashSet<AbstractInsnNode>();
 701  223
                 successors.put(node, set);
 702  
             }
 703  225
             set.add(instructions.get(successor));
 704  225
         }
 705  
 
 706  
     }
 707  
 
 708  
 }