Home | History | Annotate | Download | only in instr
      1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
      2  *
      3  * This program and the accompanying materials are made available under
      4  * the terms of the Common Public License v1.0 which accompanies this distribution,
      5  * and is available at http://www.eclipse.org/legal/cpl-v10.html
      6  *
      7  * $Id: InstrVisitor.java,v 1.1.1.1.2.4 2004/07/16 23:32:28 vlad_r Exp $
      8  */
      9 package com.vladium.emma.instr;
     10 
     11 import java.io.IOException;
     12 import java.util.ArrayList;
     13 import java.util.Arrays;
     14 import java.util.Comparator;
     15 import java.util.Iterator;
     16 import java.util.List;
     17 
     18 import com.vladium.jcd.cls.*;
     19 import com.vladium.jcd.cls.attribute.*;
     20 import com.vladium.jcd.cls.constant.CONSTANT_Class_info;
     21 import com.vladium.jcd.cls.constant.CONSTANT_Long_info;
     22 import com.vladium.jcd.cls.constant.CONSTANT_Methodref_info;
     23 import com.vladium.jcd.cls.constant.CONSTANT_String_info;
     24 import com.vladium.jcd.compiler.CodeGen;
     25 import com.vladium.jcd.lib.Types;
     26 import com.vladium.jcd.opcodes.IOpcodes;
     27 import com.vladium.logging.Logger;
     28 import com.vladium.util.ByteArrayOStream;
     29 import com.vladium.util.IConstants;
     30 import com.vladium.util.IntIntMap;
     31 import com.vladium.util.IntObjectMap;
     32 import com.vladium.util.IntSet;
     33 import com.vladium.util.asserts.$assert;
     34 import com.vladium.emma.IAppConstants;
     35 import com.vladium.emma.data.ClassDescriptor;
     36 import com.vladium.emma.data.CoverageOptions;
     37 import com.vladium.emma.data.IMetadataConstants;
     38 import com.vladium.emma.data.MethodDescriptor;
     39 
     40 // ----------------------------------------------------------------------------
     41 /**
     42  * @author Vlad Roubtsov, (C) 2003
     43  */
     44 public
     45 final class InstrVisitor extends AbstractClassDefVisitor
     46                          implements IClassDefVisitor, IAttributeVisitor, IOpcodes, IConstants
     47 {
     48     // public: ................................................................
     49 
     50     // TODO: m_instrument is unused
     51 
     52     public static final class InstrResult
     53     {
     54         public boolean m_instrumented;
     55         public ClassDescriptor m_descriptor;
     56 
     57     } // end of nested class
     58 
     59     public InstrVisitor (final CoverageOptions options)
     60     {
     61         m_excludeSyntheticMethods = options.excludeSyntheticMethods ();
     62         m_excludeBridgeMethods = options.excludeBridgeMethods ();
     63         m_doSUIDCompensation = options.doSUIDCompensation ();
     64 
     65         m_log = Logger.getLogger ();
     66     }
     67 
     68     /**
     69      * Analyzes 'cls' and/or instruments it for coverage:
     70      * <ul>
     71      *  <li> if 'instrument' is true, the class definition is instrumented for
     72      *       coverage if that is feasible
     73      *  <li> if 'metadata' is true, the class definition is analysed
     74      *       to create a {@link ClassDescriptor} for the original class definition
     75      * </ul>
     76      * This method returns null if 'metadata' is 'false' *or* if 'cls' is an
     77      * interface [the latter precludes coverage of interface static
     78      * initializers and may be removed in the future].<P>
     79      *
     80      * NOTE: if 'instrument' is 'true', the caller should always assume that 'cls'
     81      * has been mutated by this method even if it returned null. The caller should
     82      * then revert to the original class definition that was created as a
     83      * <code>cls.clone()</code> or by retaining the original definition bytes.
     84      * This part of contract is for efficienty and also simplifies the implementation.
     85      */
     86     public void process (final ClassDef cls,
     87                          final boolean ignoreAlreadyInstrumented,
     88                          final boolean instrument, final boolean metadata,
     89                          final InstrResult out)
     90     {
     91         out.m_instrumented = false;
     92         out.m_descriptor = null;
     93 
     94         if (! (instrument || metadata)) return; // nothing to do
     95 
     96         if (cls.isInterface ())
     97             return; // skip interfaces [may change in the future]
     98         else
     99         {
    100             reset ();
    101 
    102             m_cls = cls;
    103 
    104             // TODO: handle classes that cannot be instrumented due to bytecode/JVM limitations
    105             m_instrument = instrument;
    106             m_metadata = metadata;
    107             m_ignoreAlreadyInstrumented = ignoreAlreadyInstrumented;
    108 
    109             // TODO: create 'no instrumentation' execution path here
    110 
    111             visit ((ClassDef) null, null); // potentially changes m_instrument and m_metadata
    112 
    113             if (m_metadata)
    114             {
    115                 setClassName (cls.getName ());
    116 
    117                 out.m_descriptor = new ClassDescriptor (m_classPackageName, m_className, m_classSignature, m_classSrcFileName, m_classMethodDescriptors);
    118             }
    119 
    120             out.m_instrumented = m_instrument;
    121         }
    122     }
    123 
    124 
    125     // IClassDefVisitor:
    126 
    127     public Object visit (final ClassDef ignore, final Object ctx)
    128     {
    129         final ClassDef cls = m_cls;
    130         final String clsVMName = cls.getName ();
    131         final String clsName = Types.vmNameToJavaName (clsVMName);
    132 
    133         final boolean trace1 = m_log.atTRACE1 ();
    134         if (trace1) m_log.trace1 ("visit", "class: [" + clsVMName + "]");
    135 
    136 
    137         // skip synthetic classes if enabled:
    138         if (SKIP_SYNTHETIC_CLASSES && cls.isSynthetic ())
    139         {
    140             m_instrument = false;
    141             m_metadata = false;
    142 
    143             if (trace1) m_log.trace1 ("visit", "skipping synthetic class");
    144             return ctx;
    145         }
    146 
    147         // TODO: ideally, this check should be done in outer scope somewhere
    148         if (! m_warningIssued && clsName.startsWith (IAppConstants.APP_PACKAGE))
    149         {
    150             m_warningIssued = true;
    151 
    152             m_log.warning (IAppConstants.APP_NAME + " classes appear to be included on the instrumentation");
    153             m_log.warning ("path: this is not a correct way to use " + IAppConstants.APP_NAME);
    154         }
    155 
    156         // field uniqueness check done to detect double instrumentation:
    157         {
    158             final int [] existing = cls.getFields (COVERAGE_FIELD_NAME);
    159             if (existing.length > 0)
    160             {
    161                 m_instrument = false;
    162                 m_metadata = false;
    163 
    164                 if (m_ignoreAlreadyInstrumented)
    165                 {
    166                     if (trace1) m_log.trace1 ("visit", "skipping instrumented class");
    167                     return ctx;
    168                 }
    169                 else
    170                 {
    171                     // TODO: use a app coded exception
    172                     throw new IllegalStateException ("class [" + clsName + "] appears to be instrumented already");
    173                 }
    174             }
    175         }
    176 
    177         final IConstantCollection constants = cls.getConstants ();
    178 
    179         SyntheticAttribute_info syntheticMarker = null;
    180 
    181         // cache the location of "Synthetic" string:
    182         {
    183             if (MARK_ADDED_ELEMENTS_SYNTHETIC)
    184                 m_syntheticStringIndex = cls.addCONSTANT_Utf8 (Attribute_info.ATTRIBUTE_SYNTHETIC, true);
    185         }
    186 
    187         // add a Fieldref for the runtime coverage collector field:
    188         {
    189             // note: this is a bit premature if the class has no methods that need
    190             // instrumentation
    191             // TODO: the mutated version is easily discardable; however, this case
    192             // needs attention at metadata/report generation level
    193 
    194             final int coverageFieldOffset;
    195             final String fieldDescriptor = "[[Z";
    196 
    197             // note that post-4019 builds can modify this field outside of <clinit> (although
    198             // it can only happen as part of initializing a set of classes); however, it is legal
    199             // to declare this field final:
    200 
    201             final int fieldModifiers = IAccessFlags.ACC_PRIVATE | IAccessFlags.ACC_STATIC | IAccessFlags.ACC_FINAL;
    202 
    203             // add declared field:
    204             if (MARK_ADDED_ELEMENTS_SYNTHETIC)
    205             {
    206                 final IAttributeCollection fieldAttributes = ElementFactory.newAttributeCollection (1);
    207 
    208                 syntheticMarker = new SyntheticAttribute_info (m_syntheticStringIndex);
    209                 fieldAttributes.add (syntheticMarker);
    210 
    211                 coverageFieldOffset = cls.addField (COVERAGE_FIELD_NAME, fieldDescriptor,
    212                     fieldModifiers, fieldAttributes);
    213             }
    214             else
    215             {
    216                 coverageFieldOffset = cls.addField (COVERAGE_FIELD_NAME, fieldDescriptor,
    217                     fieldModifiers);
    218             }
    219 
    220             //add fieldref:
    221             m_coverageFieldrefIndex = cls.addFieldref (coverageFieldOffset);
    222         }
    223 
    224         // add a Methodref for Runtime.r():
    225         {
    226             // TODO: compute this without loading Runtime Class?
    227             final String classJVMName = "com/vladium/emma/rt/RT";
    228             final int class_index = cls.addClassref (classJVMName);
    229 
    230             // NOTE: keep this descriptor in sync with the actual signature
    231             final String methodDescriptor = "([[ZLjava/lang/String;J)V";
    232             final int nametype_index = cls.addNameType ("r", methodDescriptor);
    233 
    234             m_registerMethodrefIndex = constants.add (new CONSTANT_Methodref_info (class_index, nametype_index));
    235         }
    236 
    237         // SF FR 971186: split the init logic into a separate method so it could
    238         // be called from regular method headers if necessary:
    239 
    240         // add a Methodref for pre-<clinit> method:
    241         {
    242             // NOTE: keep this descriptor in sync with the actual signature
    243             final String methodDescriptor = "()[[Z";
    244             final int nametype_index = cls.addNameType (PRECLINIT_METHOD_NAME, methodDescriptor);
    245 
    246             m_preclinitMethodrefIndex = constants.add (new CONSTANT_Methodref_info (cls.getThisClassIndex (), nametype_index));
    247         }
    248 
    249         // add a CONSTANT_String that corresponds to the class name [in JVM format]:
    250         {
    251             m_classNameConstantIndex = constants.add (new CONSTANT_String_info (cls.getThisClass ().m_name_index));
    252         }
    253 
    254         // visit method collection:
    255         visit (cls.getMethods (), ctx);
    256 
    257         // if necessary, do SUID compensation [need to be done after method
    258         // visits when it is known whether a <clinit> was added]:
    259         if (m_doSUIDCompensation)
    260         {
    261             // compensation not necessary if the original clsdef already defined <clinit>:
    262             boolean compensate = ((m_clinitStatus & IMetadataConstants.METHOD_ADDED) != 0);
    263 
    264             int existingSUIDFieldCount = 0;
    265 
    266             if (compensate)
    267             {
    268                 // compensation not necessary if the original clsdef already controlled it via 'serialVersionUID':
    269                 {
    270                     final int [] existing = cls.getFields (SUID_FIELD_NAME);
    271                     existingSUIDFieldCount = existing.length;
    272 
    273                     if (existingSUIDFieldCount > 0)
    274                     {
    275                         final IFieldCollection fields = cls.getFields ();
    276 
    277                         for (int f = 0; f < existingSUIDFieldCount; ++ f)
    278                         {
    279                             final Field_info field = fields.get (existing [f]);
    280                             if ((field.getAccessFlags () & (IAccessFlags.ACC_STATIC | IAccessFlags.ACC_FINAL))
    281                                  == (IAccessFlags.ACC_STATIC | IAccessFlags.ACC_FINAL))
    282                             {
    283                                 // TODO: should also check for presence of a non-zero initializer
    284 
    285                                 compensate = false;
    286                                 break;
    287                             }
    288                         }
    289                     }
    290                 }
    291 
    292                 // compensation not necessary if we can determine that this class
    293                 // does not implement java.io.Serializable/Externalizable:
    294 
    295                 if (compensate && (cls.getThisClassIndex () == 0)) // no superclasses [this tool can't traverse inheritance chains]
    296                 {
    297                     boolean serializable = false;
    298 
    299                     final IInterfaceCollection interfaces = cls.getInterfaces ();
    300                     for (int i = 0, iLimit = interfaces.size (); i < iLimit; ++ i)
    301                     {
    302                         final CONSTANT_Class_info ifc = (CONSTANT_Class_info) constants.get (interfaces.get (i));
    303                         final String ifcName = ifc.getName (cls);
    304                         if (JAVA_IO_SERIALIZABLE_NAME.equals (ifcName) || JAVA_IO_EXTERNALIZABLE_NAME.equals (ifcName))
    305                         {
    306                             serializable = true;
    307                             break;
    308                         }
    309                     }
    310 
    311                     if (! serializable) compensate = false;
    312                 }
    313             }
    314 
    315             if (compensate)
    316             {
    317                 if (existingSUIDFieldCount > 0)
    318                 {
    319                     // if we get here, the class declares a 'serialVersionUID' field
    320                     // that is not both static and final and/or is not initialized
    321                     // statically: warn that SUID compensation may not work
    322 
    323                     m_log.warning ("class [" + clsName + "] declares a 'serialVersionUID'");
    324                     m_log.warning ("field that is not static and final: this is likely an implementation mistake");
    325                     m_log.warning ("and can interfere with " + IAppConstants.APP_NAME + "'s SUID compensation");
    326                 }
    327 
    328                 final String fieldDescriptor = "J";
    329                 final int fieldModifiers = IAccessFlags.ACC_PRIVATE | IAccessFlags.ACC_STATIC | IAccessFlags.ACC_FINAL;
    330                 final IAttributeCollection fieldAttributes = ElementFactory.newAttributeCollection (MARK_ADDED_ELEMENTS_SYNTHETIC ? 2 : 1);
    331 
    332                 final int nameIndex = cls.addCONSTANT_Utf8 (Attribute_info.ATTRIBUTE_CONSTANT_VALUE, true);
    333                 final int valueIndex = constants.add (new CONSTANT_Long_info (cls.computeSUID (true))); // ignore the added <clinit>
    334 
    335                 final ConstantValueAttribute_info initializer = new ConstantValueAttribute_info (nameIndex, valueIndex);
    336                 fieldAttributes.add (initializer);
    337 
    338                 if (MARK_ADDED_ELEMENTS_SYNTHETIC)
    339                 {
    340                     if (syntheticMarker == null) syntheticMarker = new SyntheticAttribute_info (m_syntheticStringIndex);
    341                     fieldAttributes.add (syntheticMarker);
    342                 }
    343 
    344                 cls.addField (SUID_FIELD_NAME, fieldDescriptor, fieldModifiers, fieldAttributes);
    345             }
    346 
    347         } // if (m_doSUIDCompensation)
    348 
    349         // visit class attributes [to get src file name, etc]:
    350         visit (cls.getAttributes (), ctx);
    351 
    352         return ctx;
    353     }
    354 
    355 
    356     public Object visit (final IMethodCollection methods, final Object ctx)
    357     {
    358         final ClassDef cls = m_cls;
    359 
    360         final boolean trace2 = m_log.atTRACE2 ();
    361 
    362         final int originalMethodCount = methods.size ();
    363         final boolean constructMetadata = m_metadata;
    364 
    365         // create block count map: TODO: is the extra slot really needed?
    366         // - create [potentially unused] slot for added <clinit>
    367         m_classBlockCounts = new int [originalMethodCount + 1];
    368 
    369         if (constructMetadata)
    370         {
    371             // prepare to collect metadata:
    372             m_classBlockMetadata = new int [originalMethodCount + 1] [] []; // same comments as above
    373 
    374             m_classMethodDescriptors = new MethodDescriptor [originalMethodCount];
    375         }
    376 
    377 
    378         // visit each original method:
    379 
    380         for (int m = 0; m < originalMethodCount; ++ m)
    381         {
    382             final Method_info method = methods.get (m);
    383             m_methodName = method.getName (cls);
    384             if (trace2) m_log.trace2 ("visit", (method.isSynthetic () ? "synthetic " : "") + "method #" + m + ": [" + m_methodName + "]");
    385 
    386             final boolean isClinit = IClassDefConstants.CLINIT_NAME.equals (m_methodName);
    387 
    388             // TODO: research whether synthetic methods add nontrivially to line coverage or not
    389 
    390             boolean excluded = false;
    391 
    392             if (! isClinit)
    393             {
    394                 if (m_excludeSyntheticMethods && method.isSynthetic ())
    395                 {
    396                     excluded = true;
    397                     if (trace2) m_log.trace2 ("visit", "skipped synthetic method");
    398                 }
    399                 else if (m_excludeBridgeMethods && method.isBridge ())
    400                 {
    401                     excluded = true;
    402                     if (trace2) m_log.trace2 ("visit", "skipped bridge method");
    403                 }
    404             }
    405 
    406             if (excluded)
    407             {
    408                 if (constructMetadata)
    409                 {
    410                     m_classMethodDescriptors [m] = new MethodDescriptor (m_methodName, method.getDescriptor (cls), IMetadataConstants.METHOD_EXCLUDED, m_methodBlockSizes, null, 0);
    411                 }
    412             }
    413             else
    414             {
    415                 if ((method.getAccessFlags () & (IAccessFlags.ACC_ABSTRACT | IAccessFlags.ACC_NATIVE)) != 0)
    416                 {
    417                     if (constructMetadata)
    418                     {
    419                         m_classMethodDescriptors [m] = new MethodDescriptor (m_methodName, method.getDescriptor (cls), IMetadataConstants.METHOD_ABSTRACT_OR_NATIVE, m_methodBlockSizes, null, 0);
    420                     }
    421 
    422                     if (trace2) m_log.trace2 ("visit", "skipped " + (method.isAbstract () ? "abstract" : "native") + " method");
    423                 }
    424                 else // this is a regular, non-<clinit> method that has bytecode:
    425                 {
    426                     // reset first line:
    427                     m_methodFirstLine = 0;
    428 
    429                     // set current method ID:
    430                     m_methodID = m;
    431 
    432                     if (isClinit)
    433                     {
    434                         // if <clinit> found: note the ID but delay processing until the very end
    435                         m_clinitID = m;
    436                         if (trace2) m_log.trace2 ("visit", "<clinit> method delayed");
    437                     }
    438                     else
    439                     {
    440                         // visit attributes [skip visit (IAttributeCollection) method]:
    441                         final IAttributeCollection attributes = method.getAttributes ();
    442                         final int attributeCount = attributes.size ();
    443                         for (int a = 0; a < attributeCount; ++ a)
    444                         {
    445                             final Attribute_info attribute = attributes.get (a);
    446                             attribute.accept (this, ctx);
    447                         }
    448 
    449                         if (constructMetadata)
    450                         {
    451                             if ($assert.ENABLED) $assert.ASSERT (m_classBlockCounts [m_methodID] > 0, "invalid block count for method " + m_methodID + ": " + m_classBlockCounts [m_methodID]);
    452                             if ($assert.ENABLED) $assert.ASSERT (m_methodBlockSizes != null && m_methodBlockSizes.length == m_classBlockCounts [m_methodID], "invalid block sizes map for method " + m_methodID);
    453 
    454                             final int [][] methodBlockMetadata = m_classBlockMetadata [m_methodID];
    455                             final int status = (methodBlockMetadata == null ? IMetadataConstants.METHOD_NO_LINE_NUMBER_TABLE : 0);
    456 
    457                             m_classMethodDescriptors [m] = new MethodDescriptor (m_methodName, method.getDescriptor (cls), status, m_methodBlockSizes, methodBlockMetadata, m_methodFirstLine);
    458                         }
    459                     }
    460                 }
    461             }
    462         }
    463 
    464         // add <clinit> (and instrument if needed) [a <clinit> is always needed
    465         // even if there are no other instrumented method to act as a load hook]:
    466 
    467         final boolean instrumentClinit = false; // TODO: make use of this [to limit instrumentation to clinitHeader only], take into account whether we added and whether it is synthetic
    468         final Method_info clinit;
    469 
    470         if (m_clinitID >= 0)
    471         {
    472             // <clinit> existed in the original class: needs to be covered
    473 
    474             // m_clinitStatus = 0;
    475             clinit = methods.get (m_clinitID);
    476 
    477             m_classInstrMethodCount = originalMethodCount;
    478         }
    479         else
    480         {
    481             // there is no <clinit> defined by the original class: add one [and mark it synthetic]
    482 
    483             m_clinitStatus = IMetadataConstants.METHOD_ADDED;  // mark as added by us
    484 
    485             final int attribute_name_index = cls.addCONSTANT_Utf8 (Attribute_info.ATTRIBUTE_CODE, true);
    486             final int name_index = cls.addCONSTANT_Utf8 (IClassDefConstants.CLINIT_NAME, true);
    487             final int descriptor_index = cls.addCONSTANT_Utf8 ("()V", true);
    488 
    489             final IAttributeCollection attributes;
    490 
    491             if (MARK_ADDED_ELEMENTS_SYNTHETIC)
    492                 attributes = ElementFactory.newAttributeCollection (2);
    493             else
    494                 attributes = ElementFactory.newAttributeCollection (1);
    495 
    496             final CodeAttribute_info code = new CodeAttribute_info (attribute_name_index,
    497                 0, 0,
    498                 new byte [] {(byte) _return},
    499                 AttributeElementFactory.newExceptionHandlerTable (0),
    500                 ElementFactory.newAttributeCollection (0));
    501 
    502             attributes.add (code);
    503 
    504             if (MARK_ADDED_ELEMENTS_SYNTHETIC)
    505             {
    506                 attributes.add (new SyntheticAttribute_info (m_syntheticStringIndex));
    507             }
    508 
    509             clinit = new Method_info (IAccessFlags.ACC_STATIC | IAccessFlags.ACC_PRIVATE, name_index, descriptor_index, attributes);
    510 
    511             m_clinitID = cls.addMethod (clinit);
    512 
    513             if (trace2) m_log.trace2 ("visit", "added synthetic <clinit> method");
    514 
    515             // TODO: this should exclude <clinit> if it were added by us
    516             m_classInstrMethodCount = originalMethodCount + 1;
    517         }
    518 
    519         if ($assert.ENABLED) $assert.ASSERT (m_classInstrMethodCount >= 0,
    520             "m_classInstrMethodCount not set");
    521 
    522 
    523         // visit <clinit>:
    524         {
    525             m_methodFirstLine = 0;
    526             m_methodID = m_clinitID;
    527 
    528             if (trace2) m_log.trace2 ("visit", (clinit.isSynthetic () ? "synthetic " : "") + "method #" + m_methodID + ": [<clinit>]");
    529 
    530             final IAttributeCollection attributes = clinit.getAttributes ();
    531             final int attributeCount = attributes.size ();
    532             for (int a = 0; a < attributeCount; ++ a)
    533             {
    534                 final Attribute_info attribute = attributes.get (a);
    535                 attribute.accept (this, ctx);
    536             }
    537         }
    538 
    539         // add pre-<clinit> method:
    540 
    541         {
    542             final int attribute_name_index = cls.addCONSTANT_Utf8 (Attribute_info.ATTRIBUTE_CODE, true);
    543             final int name_index = cls.addCONSTANT_Utf8 (PRECLINIT_METHOD_NAME, false);
    544             final int descriptor_index = cls.addCONSTANT_Utf8 ("()[[Z", false);
    545 
    546             final IAttributeCollection attributes;
    547 
    548             if (MARK_ADDED_ELEMENTS_SYNTHETIC)
    549                 attributes = ElementFactory.newAttributeCollection (2);
    550             else
    551                 attributes = ElementFactory.newAttributeCollection (1);
    552 
    553             final ByteArrayOStream buf = new ByteArrayOStream (PRECLINIT_INIT_CAPACITY);
    554             {
    555                 final int [] blockCounts = m_classBlockCounts;
    556                 final int instrMethodCount = m_classInstrMethodCount; // actual number of methods to instrument may be less than the size of the block map
    557 
    558                 if ($assert.ENABLED) $assert.ASSERT (blockCounts != null && blockCounts.length >= instrMethodCount,
    559                     "invalid block count map");
    560 
    561                 // new and set COVERAGE_FIELD:
    562 
    563                 // push first dimension:
    564                 CodeGen.push_int_value (buf, cls, instrMethodCount);
    565 
    566                 // [stack +1]
    567 
    568                 // new boolean [][]:
    569                 final int type_index = cls.addClassref ("[[Z");
    570                 buf.write4 (_multianewarray,
    571                             type_index >>> 8,    // indexbyte1
    572                             type_index,          // indexbyte2
    573                             1); // only one dimension created here
    574 
    575                 // [stack +1]
    576 
    577                 // clone array ref:
    578                 buf.write4 (_dup,
    579 
    580                 // [stack +2]
    581 
    582                 // store in the static field
    583                             _putstatic,
    584                             m_coverageFieldrefIndex >>> 8,    // indexbyte1
    585                             m_coverageFieldrefIndex);          // indexbyte2
    586 
    587                 // [stack +1]
    588 
    589                 for (int m = 0; m < instrMethodCount; ++ m)
    590                 {
    591                     final int blockCount = blockCounts [m];
    592                     if (blockCount > 0)
    593                     {
    594                         // clone array ref:
    595                         buf.write (_dup);
    596 
    597                         // [stack +2]
    598 
    599                         // push outer dim index:
    600                         CodeGen.push_int_value (buf, cls, m);
    601 
    602                         // [stack +3]
    603 
    604                         // push dim:
    605                         CodeGen.push_int_value (buf, cls, blockCount);
    606 
    607                         // [stack +4]
    608 
    609                         // newarray boolean []:
    610                         buf.write3 (_newarray,
    611                                     4, // "T_BOOLEAN"
    612 
    613                         // add subarray to the outer array:
    614                                     _aastore);
    615 
    616                         // [stack +1]
    617                     }
    618                 }
    619 
    620                 // [stack +1]
    621 
    622                 {
    623                     // clone array ref
    624                     buf.write (_dup);
    625 
    626                     // [stack +2]
    627 
    628                     CodeGen.push_constant_index (buf, m_classNameConstantIndex);
    629 
    630                     // [stack +3]
    631 
    632                     buf.write3 (_ldc2_w,
    633                                 m_stampIndex >>> 8,    // indexbyte1
    634                                 m_stampIndex);         // indexbyte2
    635 
    636                     // [stack +5]
    637 
    638                     buf.write3 (_invokestatic,
    639                                 m_registerMethodrefIndex >>> 8,    // indexbyte1
    640                                 m_registerMethodrefIndex);         // indexbyte2
    641 
    642                     // [stack +1]
    643                 }
    644 
    645                 // pop and return extra array ref:
    646                 buf.write (_areturn);
    647 
    648                 // [stack +0]
    649             }
    650 
    651             final CodeAttribute_info code = new CodeAttribute_info (attribute_name_index,
    652                 5, 0, // adjust constants if the bytecode emitted above changes
    653                 EMPTY_BYTE_ARRAY,
    654                 AttributeElementFactory.newExceptionHandlerTable (0),
    655                 ElementFactory.newAttributeCollection (0));
    656 
    657             code.setCode (buf.getByteArray (), buf.size ());
    658 
    659             attributes.add (code);
    660 
    661             if (MARK_ADDED_ELEMENTS_SYNTHETIC)
    662             {
    663                 attributes.add (new SyntheticAttribute_info (m_syntheticStringIndex));
    664             }
    665 
    666             final Method_info preclinit = new Method_info (IAccessFlags.ACC_STATIC | IAccessFlags.ACC_PRIVATE, name_index, descriptor_index, attributes);
    667             cls.addMethod (preclinit);
    668 
    669             if (trace2) m_log.trace2 ("visit", "added synthetic pre-<clinit> method");
    670         }
    671 
    672 
    673         if (constructMetadata)
    674         {
    675             if ($assert.ENABLED) $assert.ASSERT (m_classBlockCounts [m_methodID] > 0, "invalid block count for method " + m_methodID + " (" + IClassDefConstants.CLINIT_NAME + "): " + m_classBlockCounts [m_methodID]);
    676             if ($assert.ENABLED) $assert.ASSERT (m_methodBlockSizes != null && m_methodBlockSizes.length == m_classBlockCounts [m_methodID], "invalid block sizes map for method " + m_methodID);
    677 
    678             final int [][] methodBlockMetadata = m_classBlockMetadata [m_methodID];
    679             m_clinitStatus |= (methodBlockMetadata == null ? IMetadataConstants.METHOD_NO_LINE_NUMBER_TABLE : 0);
    680 
    681             // TODO: this still does not process not added/synthetic case
    682 
    683             if ((m_clinitStatus & IMetadataConstants.METHOD_ADDED) == 0)
    684                 m_classMethodDescriptors [m_methodID] = new MethodDescriptor (IClassDefConstants.CLINIT_NAME, clinit.getDescriptor (cls), m_clinitStatus, m_methodBlockSizes, methodBlockMetadata, m_methodFirstLine);
    685         }
    686 
    687         return ctx;
    688     }
    689 
    690 
    691     public Object visit (final IAttributeCollection attributes, Object ctx)
    692     {
    693         for (int a = 0, aCount = attributes.size (); a < aCount; ++ a)
    694         {
    695             // TODO: define a global way to set the mask set of attrs to be visited
    696             attributes.get (a).accept (this, ctx);
    697         }
    698 
    699         return ctx;
    700     }
    701 
    702 
    703     // IAttributeVisitor:
    704 
    705     public Object visit (final CodeAttribute_info attribute, final Object ctx)
    706     {
    707         final boolean trace2 = m_log.atTRACE2 ();
    708         final boolean trace3 = m_log.atTRACE3 ();
    709 
    710         final byte [] code = attribute.getCode ();
    711         final int codeSize = attribute.getCodeSize ();
    712 
    713         if (trace2) m_log.trace2 ("visit", "code attribute for method #" + m_methodID + ": size = " + codeSize);
    714 
    715         final IntSet leaders = new IntSet ();
    716 
    717         // instructionMap.get(ip) is the number of instructions in code[0-ip)
    718         // [this map will include a mapping for code length as well]
    719         final IntIntMap /* int(ip)->instr count */ instructionMap = new IntIntMap ();
    720 
    721         // add first instruction and all exc handler start pcs:
    722         leaders.add (0);
    723 
    724         final IExceptionHandlerTable exceptions = attribute.getExceptionTable ();
    725         final int exceptionCount = exceptions.size ();
    726         for (int e = 0; e < exceptionCount; ++ e)
    727         {
    728             final Exception_info exception = exceptions.get (e);
    729             leaders.add (exception.m_handler_pc);
    730         }
    731 
    732 
    733         final IntObjectMap branches = new IntObjectMap ();
    734 
    735         // determine block leaders [an O(code length) loop]:
    736 
    737         boolean branch = false;
    738         boolean wide = false;
    739 
    740         int instructionCount = 0;
    741         instructionMap.put (0, 0);
    742 
    743         for (int ip = 0; ip < codeSize; )
    744         {
    745             final int opcode = 0xFF & code [ip];
    746             int size = 0; // will be set to -<real size> for special cases in the switch below
    747 
    748             //if (trace3) m_log.trace3 ("parse", MNEMONICS [opcode]);
    749             // "visitor.visit (opcode, wide, ip, null)":
    750 
    751             { // "opcode visit" logic:
    752 
    753                 int iv, ov;
    754 
    755                 if (branch)
    756                 {
    757                     // previous instruction was a branch: this one is a leader
    758                     leaders.add (ip);
    759                     branch = false;
    760                 }
    761 
    762                 switch (opcode)
    763                 {
    764                     case _ifeq:
    765                     case _iflt:
    766                     case _ifle:
    767                     case _ifne:
    768                     case _ifgt:
    769                     case _ifge:
    770                     case _ifnull:
    771                     case _ifnonnull:
    772                     case _if_icmpeq:
    773                     case _if_icmpne:
    774                     case _if_icmplt:
    775                     case _if_icmpgt:
    776                     case _if_icmple:
    777                     case _if_icmpge:
    778                     case _if_acmpeq:
    779                     case _if_acmpne:
    780                     {
    781                         //ov = getI2 (code, ip + 1);
    782                         int scan = ip + 1;
    783                         ov = (code [scan] << 8) | (0xFF & code [++ scan]);
    784 
    785                         final int target = ip + ov;
    786                         leaders.add (target);
    787 
    788                         branches.put (ip, new IFJUMP2 (opcode, target));
    789                         branch = true;
    790                     }
    791                     break;
    792 
    793 
    794                     case _goto:
    795                     case _jsr:
    796                     {
    797                         //ov = getI2 (code, ip + 1);
    798                         int scan = ip + 1;
    799                         ov = (code [scan] << 8) | (0xFF & code [++ scan]);
    800 
    801                         final int target = ip + ov;
    802                         leaders.add (target);
    803 
    804                         branches.put (ip, new JUMP2 (opcode, target));
    805                         branch = true;
    806                     }
    807                     break;
    808 
    809 
    810                     case _lookupswitch:
    811                     {
    812                         int scan = ip + 4 - (ip & 3); // eat padding
    813 
    814                         ov = (code [scan] << 24) | ((0xFF & code [++ scan]) << 16) | ((0xFF & code [++ scan]) << 8) | (0xFF & code [++ scan]);
    815                         leaders.add (ip + ov);
    816 
    817                         //final int npairs = getU4 (code, scan);
    818                         //scan += 4;
    819                         final int npairs = ((0xFF & code [++ scan]) << 24) | ((0xFF & code [++ scan]) << 16) | ((0xFF & code [++ scan]) << 8) | (0xFF & code [++ scan]);
    820 
    821                         final int [] keys = new int [npairs];
    822                         final int [] targets = new int [npairs + 1];
    823                         targets [0] = ip + ov;
    824 
    825                         for (int p = 0; p < npairs; ++ p)
    826                         {
    827                             //iv = getI4 (code, scan);
    828                             //scan += 4;
    829                             iv = (code [++ scan] << 24) | ((0xFF & code [++ scan]) << 16) | ((0xFF & code [++ scan]) << 8) | (0xFF & code [++ scan]);
    830                             keys [p] = iv;
    831 
    832 
    833                             //ov = getI4 (code, scan);
    834                             //scan += 4;
    835                             ov = (code [++ scan] << 24) | ((0xFF & code [++ scan]) << 16) | ((0xFF & code [++ scan]) << 8) | (0xFF & code [++ scan]);
    836                             targets [p + 1] = ip + ov;
    837                             leaders.add (ip + ov);
    838                         }
    839 
    840                         branches.put (ip, new LOOKUPSWITCH (keys, targets));
    841                         branch = true;
    842 
    843                         size = ip - scan - 1; // special case
    844                     }
    845                     break;
    846 
    847 
    848                     case _tableswitch:
    849                     {
    850                         int scan = ip + 4 - (ip & 3); // eat padding
    851 
    852                         ov = (code [scan] << 24) | ((0xFF & code [++ scan]) << 16) | ((0xFF & code [++ scan]) << 8) | (0xFF & code [++ scan]);
    853                         leaders.add (ip + ov);
    854 
    855                         //final int low = getI4 (code, scan + 4);
    856                         final int low = (code [++ scan] << 24) | ((0xFF & code [++ scan]) << 16) | ((0xFF & code [++ scan]) << 8) | (0xFF & code [++ scan]);
    857                         //final int high = getI4 (code, scan + 8);
    858                         //scan += 12;
    859                         final int high = (code [++ scan] << 24) | ((0xFF & code [++ scan]) << 16) | ((0xFF & code [++ scan]) << 8) | (0xFF & code [++ scan]);
    860 
    861                         final int [] targets = new int [high - low + 2];
    862                         targets [0] = ip + ov;
    863 
    864                         for (int index = low; index <= high; ++ index)
    865                         {
    866                             //ov = getI4 (code, scan);
    867                             ov = (code [++ scan] << 24) | ((0xFF & code [++ scan]) << 16) | ((0xFF & code [++ scan]) << 8) | (0xFF & code [++ scan]);
    868                             targets [index - low + 1] = ip + ov;
    869                             leaders.add (ip + ov);
    870                             //scan += 4;
    871                         }
    872 
    873                         branches.put (ip, new TABLESWITCH (low, high, targets));
    874                         branch = true;
    875 
    876                         size = ip - scan - 1; // special case
    877                     }
    878                     break;
    879 
    880 
    881                     case _goto_w:
    882                     case _jsr_w:
    883                     {
    884                         int scan = ip + 1;
    885                         //ov = getI4 (code, ip + 1);
    886                         ov = (code [scan] << 24) | ((0xFF & code [++ scan]) << 16) | ((0xFF & code [++ scan]) << 8) | (0xFF & code [++ scan]);
    887                         final int target = ip + ov;
    888 
    889                         leaders.add (target);
    890 
    891                         branches.put (ip, new JUMP4 (opcode, target));
    892                         branch = true;
    893                     }
    894                     break;
    895 
    896 
    897                     case _ret:
    898                     {
    899                         int scan = ip + 1;
    900                         iv = wide ? (((0xFF & code [scan]) << 8) | (0xFF & code [++ scan])) : (0xFF & code [scan]);
    901 
    902                         branches.put (ip, new RET (opcode, iv));
    903                         branch = true;
    904                     }
    905                     break;
    906 
    907 
    908                     case _athrow:
    909                     case _ireturn:
    910                     case _lreturn:
    911                     case _freturn:
    912                     case _dreturn:
    913                     case _areturn:
    914                     case _return:
    915                     {
    916                         branches.put (ip, new TERMINATE (opcode));
    917                         branch = true;
    918                     }
    919                     break;
    920 
    921                 } // end of switch
    922 
    923             } // end of processing the current opcode
    924 
    925 
    926             // shift to the next instruction [this is the only block that adjusts 'ip']:
    927 
    928             if (size == 0)
    929                 size = (wide ? WIDE_SIZE : NARROW_SIZE) [opcode];
    930             else
    931                 size = -size;
    932 
    933             ip += size;
    934             wide = (opcode == _wide);
    935 
    936             instructionMap.put (ip, ++ instructionCount);
    937 
    938         } // end of for
    939 
    940 
    941         // split 'code' into an ordered list of basic blocks [O(block count) loops]:
    942 
    943         final int blockCount = leaders.size ();
    944         if (trace2) m_log.trace2 ("visit", "method contains " + blockCount + " basic blocks");
    945 
    946         final BlockList blocks = new BlockList (blockCount);
    947 
    948         final int [] _leaders = new int [blockCount + 1]; // room for end-of-code leader at the end
    949         leaders.values (_leaders, 0);
    950         _leaders [blockCount] = codeSize;
    951 
    952         Arrays.sort (_leaders);
    953 
    954         final int [] _branch_locations = branches.keys ();
    955         Arrays.sort (_branch_locations);
    956 
    957         final IntIntMap leaderToBlockID = new IntIntMap (_leaders.length);
    958 
    959         if (m_metadata)
    960         {
    961             // help construct a MethodDescriptor for the current method:
    962 
    963             m_methodBlockSizes = new int [blockCount];
    964             m_methodBlockOffsets = _leaders;
    965         }
    966 
    967         // compute signature even if metadata is not needed (because the instrumented
    968         // classdef uses it):
    969         consumeSignatureData (m_methodID, _leaders);
    970 
    971         // pass 1:
    972 
    973         final int [] intHolder = new int [1];
    974         int instr_count = 0, prev_instr_count;
    975 
    976         for (int bl = 0, br = 0; bl < blockCount; ++ bl)
    977         {
    978             final Block block = new Block ();
    979             blocks.m_blocks.add (block);
    980 
    981             final int leader = _leaders [bl];
    982 
    983             block.m_first = leader; // m_first set
    984             leaderToBlockID.put (leader, bl);
    985 
    986             final int next_leader = _leaders [bl + 1];
    987             boolean branchDelimited = false;
    988 
    989             prev_instr_count = instr_count;
    990 
    991             if (_branch_locations.length > br)
    992             {
    993                 final int next_branch_location = _branch_locations [br];
    994                 if (next_branch_location < next_leader)
    995                 {
    996                     branchDelimited = true;
    997 
    998                     block.m_length = next_branch_location - leader; // m_length set
    999 
   1000                     if ($assert.ENABLED)
   1001                         $assert.ASSERT (instructionMap.get (next_branch_location, intHolder), "no mapping for " + next_branch_location);
   1002                     else
   1003                         instructionMap.get (next_branch_location, intHolder);
   1004 
   1005                     instr_count = intHolder [0] + 1; // [+ 1 for the branch]
   1006 
   1007                     block.m_branch = (Branch) branches.get (next_branch_location);
   1008                     block.m_branch.m_parentBlockID = bl; // m_branch set
   1009 
   1010                     ++ br;
   1011                 }
   1012             }
   1013 
   1014             if (! branchDelimited)
   1015             {
   1016                 block.m_length = next_leader - leader; // m_length set
   1017 
   1018                 if ($assert.ENABLED)
   1019                     $assert.ASSERT (instructionMap.get (next_leader, intHolder), "no mapping for " + next_leader);
   1020                 else
   1021                     instructionMap.get (next_leader, intHolder);
   1022 
   1023                 instr_count = intHolder [0];
   1024             }
   1025 
   1026             block.m_instrCount = instr_count - prev_instr_count; // m_instrCount set
   1027 
   1028             if ($assert.ENABLED) $assert.ASSERT (block.m_length == 0 || block.m_instrCount > 0, "invalid instr count for block " + bl + ": " + block.m_instrCount);
   1029             if (m_metadata) m_methodBlockSizes [bl] = block.m_instrCount;
   1030         }
   1031 
   1032         // pass 2:
   1033 
   1034         final Block [] _blocks = (Block []) blocks.m_blocks.toArray (new Block [blockCount]);
   1035 
   1036         for (int l = 0; l < blockCount; ++ l)
   1037         {
   1038             final Block block = _blocks [l];
   1039 
   1040             if (block.m_branch != null)
   1041             {
   1042                 final int [] targets = block.m_branch.m_targets;
   1043                 if (targets != null)
   1044                 {
   1045                     for (int t = 0, targetCount = targets.length; t < targetCount; ++ t)
   1046                     {
   1047                         // TODO: HACK ! convert block absolute offsets to block IDs:
   1048 
   1049                         if ($assert.ENABLED)
   1050                             $assert.ASSERT (leaderToBlockID.get (targets [t], intHolder), "no mapping for " + targets [t]);
   1051                         else
   1052                             leaderToBlockID.get (targets [t], intHolder);
   1053 
   1054                         targets [t] = intHolder [0];
   1055                     }
   1056                 }
   1057             }
   1058         }
   1059 
   1060 
   1061         // update block count map [used later by <clinit> visit]:
   1062         m_classBlockCounts [m_methodID] = blockCount;
   1063 
   1064         // actual basic block instrumentation:
   1065         {
   1066             if (trace2) m_log.trace2 ("visit", "instrumenting... ");
   1067 
   1068             // determine the local var index for the var that will alias COVERAGE_FIELD:
   1069             final int localVarIndex = attribute.m_max_locals ++;
   1070 
   1071             if (m_methodID == m_clinitID) // note: m_clinitID can be -1 if <clinit> has not been visited yet
   1072             {
   1073                  // add a long stamp constant after all the original methods have been visited:
   1074 
   1075                 m_stampIndex = m_cls.getConstants ().add (new CONSTANT_Long_info (m_classSignature));
   1076 
   1077                 blocks.m_header = new clinitHeader (this, localVarIndex);
   1078             }
   1079             else
   1080                 blocks.m_header = new methodHeader (this, localVarIndex);
   1081 
   1082             int headerMaxStack = blocks.m_header.maxstack ();
   1083             int methodMaxStack = 0;
   1084 
   1085             for (int l = 0; l < blockCount; ++ l)
   1086             {
   1087                 final Block block = _blocks [l];
   1088 
   1089                 final CodeSegment insertion = new BlockSegment (this, localVarIndex, l);
   1090                 block.m_insertion = insertion;
   1091 
   1092                 final int insertionMaxStack = insertion.maxstack ();
   1093                 if (insertionMaxStack > methodMaxStack)
   1094                     methodMaxStack = insertionMaxStack;
   1095             }
   1096 
   1097             // update maxstack as needed [it can only grow]:
   1098             {
   1099                 final int oldMaxStack = attribute.m_max_stack;
   1100 
   1101                 attribute.m_max_stack += methodMaxStack; // this is not precise, but still need to add because the insertion may be happening at the old maxstack point
   1102 
   1103                 if (headerMaxStack > attribute.m_max_stack)
   1104                 attribute.m_max_stack = headerMaxStack;
   1105 
   1106                 if (trace3) m_log.trace3 ("visit", "increasing maxstack by " + (attribute.m_max_stack - oldMaxStack));
   1107             }
   1108 
   1109             if ($assert.ENABLED) $assert.ASSERT (blocks.m_header != null, "header not set");
   1110         }
   1111 
   1112 
   1113         // assemble all blocks into an instrumented code block:
   1114         if (trace2) m_log.trace2 ("visit", "assembling... ");
   1115 
   1116         int newcodeCapacity = codeSize << 1;
   1117         if (newcodeCapacity < EMIT_CTX_MIN_INIT_CAPACITY) newcodeCapacity = EMIT_CTX_MIN_INIT_CAPACITY;
   1118 
   1119         final ByteArrayOStream newcode = new ByteArrayOStream (newcodeCapacity); // TODO: empirical capacity
   1120         final EmitCtx emitctx = new EmitCtx (blocks, newcode);
   1121 
   1122         // create a jump adjustment map:
   1123         final int [] jumpAdjOffsets = new int [blockCount]; // room for initial 0  + (blockCount - 1)
   1124         final int [] jumpAdjMap = new int [jumpAdjOffsets.length]; // room for initial 0  + (blockCount - 1)
   1125 
   1126         if ($assert.ENABLED) $assert.ASSERT (jumpAdjOffsets.length == jumpAdjMap.length,
   1127             "jumpAdjOffsets and jumpAdjMap length mismatch");
   1128 
   1129         // header:
   1130         blocks.m_header.emit (emitctx);
   1131         // jumpAdjOffsets [0] = 0: redundant
   1132         jumpAdjMap [0] = emitctx.m_out.size ();
   1133 
   1134         // rest of blocks:
   1135         for (int l = 0; l < blockCount; ++ l)
   1136         {
   1137             final Block block = _blocks [l];
   1138 
   1139             if (l + 1 < blockCount)
   1140             {
   1141                 jumpAdjOffsets [l + 1] = _blocks [l].m_first + _blocks [l].m_length; // implies the insertion goes just before the branch
   1142             }
   1143 
   1144             block.emit (emitctx, code);
   1145 
   1146             // TODO: this breaks if code can shrink:
   1147             if (l + 1 < blockCount)
   1148             {
   1149                 jumpAdjMap [l + 1] = emitctx.m_out.size () - _blocks [l + 1].m_first;
   1150             }
   1151         }
   1152 
   1153         m_methodJumpAdjOffsets = jumpAdjOffsets;
   1154         m_methodJumpAdjValues = jumpAdjMap;
   1155 
   1156         if (trace3)
   1157         {
   1158             final StringBuffer s = new StringBuffer ("jump adjustment map:" + EOL);
   1159             for (int a = 0; a < jumpAdjOffsets.length; ++ a)
   1160             {
   1161                 s.append ("    " + jumpAdjOffsets [a] + ": +" + jumpAdjMap [a]);
   1162                 if (a < jumpAdjOffsets.length - 1) s.append (EOL);
   1163             }
   1164 
   1165             m_log.trace3 ("visit", s.toString ());
   1166         }
   1167 
   1168         final byte [] _newcode = newcode.getByteArray (); // note: not cloned
   1169         final int _newcodeSize = newcode.size ();
   1170 
   1171         // [all blocks have had their m_first adjusted]
   1172 
   1173         // backpatching pass:
   1174         if (trace3) m_log.trace3 ("visit", "backpatching " + emitctx.m_backpatchQueue.size () + " ip(s)");
   1175 
   1176         for (Iterator i = emitctx.m_backpatchQueue.iterator (); i.hasNext (); )
   1177         {
   1178             final int [] patchData = (int []) i.next ();
   1179             int ip = patchData [1];
   1180 
   1181             if ($assert.ENABLED) $assert.ASSERT (patchData != null, "null patch data for ip " + ip);
   1182 
   1183             final int jump = _blocks [patchData [3]].m_first - patchData [2];
   1184             if ($assert.ENABLED) $assert.ASSERT (jump > 0, "negative backpatch jump offset " + jump + " for ip " + ip);
   1185 
   1186             switch (patchData [0])
   1187             {
   1188                 case 4:
   1189                 {
   1190                     _newcode [ip ++] = (byte) (jump >>> 24);
   1191                     _newcode [ip ++] = (byte) (jump >>> 16);
   1192 
   1193                 } // *FALL THROUGH*
   1194 
   1195                 case 2:
   1196                 {
   1197                     _newcode [ip ++] = (byte) (jump >>> 8);
   1198                     _newcode [ip] = (byte) jump;
   1199                 }
   1200             }
   1201         }
   1202 
   1203         attribute.setCode (_newcode, _newcodeSize);
   1204         if (trace2) m_log.trace2 ("visit", "method assembled into " + _newcodeSize + " code bytes");
   1205 
   1206 
   1207         // adjust bytecode offsets in the exception table:
   1208         final IExceptionHandlerTable exceptionTable = attribute.getExceptionTable ();
   1209         for (int e = 0; e < exceptionTable.size (); ++ e)
   1210         {
   1211             final Exception_info exception = exceptionTable.get (e);
   1212 
   1213             int adjSegment = lowbound (jumpAdjOffsets, exception.m_start_pc);
   1214             exception.m_start_pc += jumpAdjMap [adjSegment];
   1215 
   1216             adjSegment = lowbound (jumpAdjOffsets, exception.m_end_pc);
   1217             exception.m_end_pc += jumpAdjMap [adjSegment];
   1218 
   1219             adjSegment = lowbound (jumpAdjOffsets, exception.m_handler_pc);
   1220             exception.m_handler_pc += jumpAdjMap [adjSegment];
   1221         }
   1222 
   1223 
   1224         // visit other nested attributes [LineNumberAttribute, etc]:
   1225         final IAttributeCollection attributes = attribute.getAttributes ();
   1226         final int attributeCount = attributes.size ();
   1227         for (int a = 0; a < attributeCount; ++ a)
   1228         {
   1229             final Attribute_info nested = attributes.get (a);
   1230             nested.accept (this, ctx);
   1231         }
   1232 
   1233         return ctx;
   1234     }
   1235 
   1236 
   1237     public Object visit (final LineNumberTableAttribute_info attribute, final Object ctx)
   1238     {
   1239         final boolean trace2 = m_log.atTRACE2 ();
   1240         final boolean trace3 = m_log.atTRACE3 ();
   1241         if (trace2) m_log.trace2 ("visit", "attribute: [" + attribute.getName (m_cls) + "]");
   1242 
   1243         final int lineCount = attribute.size ();
   1244 
   1245         if (m_metadata)
   1246         {
   1247             if (trace2) m_log.trace2 ("visit", "processing line number table for metadata...");
   1248 
   1249             final int blockCount = m_classBlockCounts [m_methodID];
   1250             if ($assert.ENABLED) $assert.ASSERT (blockCount > 0, "invalid method block count for method " + m_methodID);
   1251 
   1252             final int [][] blockLineMap = new int [blockCount][];
   1253 
   1254             if ($assert.ENABLED) $assert.ASSERT (blockCount + 1 == m_methodBlockOffsets.length,
   1255                     "invalid m_methodBlockOffsets");
   1256 
   1257             if (lineCount == 0)
   1258             {
   1259                 for (int bl = 0; bl < blockCount; ++ bl)
   1260                     blockLineMap [bl] = EMPTY_INT_ARRAY;
   1261             }
   1262             else
   1263             {
   1264                 // TODO: this code does not work if there are multiple LineNumberTableAttribute attributes for the method
   1265 
   1266                 final LineNumber_info [] sortedLines = new LineNumber_info [attribute.size ()];
   1267 
   1268                 for (int l = 0; l < lineCount; ++ l)
   1269                 {
   1270                     final LineNumber_info line = attribute.get (l);
   1271                     sortedLines [l] = line;
   1272                 }
   1273 
   1274                 Arrays.sort (sortedLines, LINE_NUMBER_COMPARATOR);
   1275 
   1276                 // construct block->line mapping: TODO: is the loop below the fastest it can be done?
   1277 
   1278                 final int [] methodBlockOffsets = m_methodBlockOffsets;
   1279 
   1280                 LineNumber_info line = sortedLines [0]; // never null
   1281                 LineNumber_info prev_line = null;
   1282 
   1283                 // remember the first line:
   1284                 m_methodFirstLine = line.m_line_number;
   1285 
   1286                 for (int bl = 0, l = 0; bl < blockCount; ++ bl)
   1287                 {
   1288                     final IntSet blockLines = new IntSet ();
   1289 
   1290                     if ((prev_line != null) && (line.m_start_pc > methodBlockOffsets [bl]))
   1291                     {
   1292                         blockLines.add (prev_line.m_line_number);
   1293                     }
   1294 
   1295                     while (line.m_start_pc < methodBlockOffsets [bl + 1])
   1296                     {
   1297                         blockLines.add (line.m_line_number);
   1298 
   1299                         if (l == lineCount - 1)
   1300                             break;
   1301                         else
   1302                         {
   1303                             prev_line = line;
   1304                             line = sortedLines [++ l]; // advance to the next line
   1305                         }
   1306                     }
   1307 
   1308                     blockLineMap [bl] = blockLines.values ();
   1309                 }
   1310             }
   1311 
   1312             m_classBlockMetadata [m_methodID] = blockLineMap;
   1313 
   1314             if (trace3)
   1315             {
   1316                 StringBuffer s = new StringBuffer ("block-line map for method #" + m_methodID + ":");
   1317                 for (int bl = 0; bl < blockCount; ++ bl)
   1318                 {
   1319                     s.append (EOL);
   1320                     s.append ("    block " + bl + ": ");
   1321 
   1322                     final int [] lines = blockLineMap [bl];
   1323                     for (int l = 0; l < lines.length; ++ l)
   1324                     {
   1325                         if (l != 0) s.append (", ");
   1326                         s.append (lines [l]);
   1327                     }
   1328                 }
   1329 
   1330                 m_log.trace3 ("visit", s.toString ());
   1331             }
   1332         }
   1333 
   1334         for (int l = 0; l < lineCount; ++ l)
   1335         {
   1336             final LineNumber_info line = attribute.get (l);
   1337 
   1338             // TODO: make this faster using either table assist or the sorted array in 'sortedLines'
   1339 
   1340             // adjust bytecode offset for line number mapping:
   1341             int adjSegment = lowbound (m_methodJumpAdjOffsets, line.m_start_pc);
   1342             line.m_start_pc += m_methodJumpAdjValues [adjSegment];
   1343         }
   1344 
   1345         return ctx;
   1346     }
   1347 
   1348     // TODO: line var table as well
   1349 
   1350 
   1351     // no-op visits:
   1352 
   1353     public Object visit (final ExceptionsAttribute_info attribute, final Object ctx)
   1354     {
   1355         return ctx;
   1356     }
   1357 
   1358     public Object visit (final ConstantValueAttribute_info attribute, final Object ctx)
   1359     {
   1360         return ctx;
   1361     }
   1362 
   1363     public Object visit (final SourceFileAttribute_info attribute, final Object ctx)
   1364     {
   1365         m_classSrcFileName = attribute.getSourceFile (m_cls).m_value;
   1366 
   1367         return ctx;
   1368     }
   1369 
   1370     public Object visit (final SyntheticAttribute_info attribute, final Object ctx)
   1371     {
   1372         return ctx;
   1373     }
   1374 
   1375     public Object visit (final BridgeAttribute_info attribute, final Object ctx)
   1376     {
   1377         return ctx;
   1378     }
   1379 
   1380     public Object visit (final InnerClassesAttribute_info attribute, final Object ctx)
   1381     {
   1382         return ctx;
   1383     }
   1384 
   1385     public Object visit (final GenericAttribute_info attribute, final Object ctx)
   1386     {
   1387         return ctx;
   1388     }
   1389 
   1390     // protected: .............................................................
   1391 
   1392     // package: ...............................................................
   1393 
   1394     // private: ...............................................................
   1395 
   1396 
   1397     private static final class BlockList
   1398     {
   1399         BlockList ()
   1400         {
   1401             m_blocks = new ArrayList ();
   1402         }
   1403 
   1404         BlockList (final int capacity)
   1405         {
   1406             m_blocks = new ArrayList (capacity);
   1407         }
   1408 
   1409         final List /* Block */ m_blocks; // TODO: might as well use an array here?
   1410         CodeSegment m_header;
   1411 
   1412     } // end of nested class
   1413 
   1414 
   1415     private static final class Block
   1416     {
   1417         int m_first;    // inclusive offset of the leader instruction [first instr in the block]
   1418         //int m_last;     // exclusive offset of the last non-branch instruction [excludes possible control transfer at the end]
   1419         int m_length;   // excluding the branch statement [can be 0]
   1420         int m_instrCount; // size in instructions, including the [optional] original branch; [m_insertion is not counted]
   1421 
   1422         // NOTE: it is possible that m_first == m_last [the block is empty except for a possible control transfer instr]
   1423 
   1424 //        public int maxlength ()
   1425 //        {
   1426 //            // TODO: cache
   1427 //            return m_length
   1428 ////                + (m_insertion != null ? m_insertion.maxlength () : 0)
   1429 //                + (m_branch != null ? m_branch.maxlength () : 0);
   1430 //        }
   1431 
   1432         /**
   1433          * When this is called, all previous blocks have been written out and
   1434          * their m_first have been updated.
   1435          */
   1436         void emit (final EmitCtx ctx, final byte [] code) // TODO: move 'code' into 'ctx'
   1437         {
   1438             final ByteArrayOStream out = ctx.m_out;
   1439             final int first = m_first;
   1440 
   1441             m_first = out.size (); // update position to be within new code array
   1442 
   1443             for (int i = 0, length = m_length; i < length; ++ i)
   1444             {
   1445                 out.write (code [first + i]);
   1446             }
   1447 
   1448             if (m_insertion != null)
   1449                 m_insertion.emit (ctx);
   1450 
   1451             if (m_branch != null)
   1452                 m_branch.emit (ctx);
   1453         }
   1454 
   1455         public CodeSegment m_insertion;
   1456         public Branch m_branch; // falling through is implied by this being null
   1457 
   1458     } // end of nested class
   1459 
   1460 
   1461     static final class EmitCtx
   1462     {
   1463         // TODO: profile to check that ByteArrayOStream.write() is not the bottleneck
   1464 
   1465         EmitCtx (final BlockList blocks, final ByteArrayOStream out)
   1466         {
   1467             m_blocks = blocks;
   1468             m_out = out;
   1469 
   1470             m_backpatchQueue = new ArrayList ();
   1471         }
   1472 
   1473         final BlockList m_blocks;
   1474         final ByteArrayOStream m_out;
   1475         final List /* int[4] */ m_backpatchQueue;
   1476 
   1477     } // end of nested class
   1478 
   1479 
   1480     /**
   1481      * A Branch does not add any maxlocals/maxstack requirements.
   1482      */
   1483     static abstract class Branch
   1484     {
   1485         protected Branch (final int opcode, final int [] targets)
   1486         {
   1487             m_opcode = (byte) opcode;
   1488             m_targets = targets;
   1489         }
   1490 
   1491         /*
   1492          * Called when targets are block IDs, before emitting.
   1493          */
   1494         int maxlength () { return 1; }
   1495 
   1496         abstract void emit (EmitCtx ctx);
   1497 
   1498         // TODO: this method must signal when it is necessary to switch to long jump form
   1499         protected final void emitJumpOffset2 (final EmitCtx ctx, final int ip, final int targetBlockID)
   1500         {
   1501             final ByteArrayOStream out = ctx.m_out;
   1502 
   1503             if (targetBlockID <= m_parentBlockID)
   1504             {
   1505                 // backwards branch:
   1506                 final int jumpOffset = ((Block) ctx.m_blocks.m_blocks.get (targetBlockID)).m_first - ip;
   1507 
   1508                 out.write2 (jumpOffset >>> 8,   // targetbyte1
   1509                             jumpOffset);         // targetbyte2
   1510             }
   1511             else
   1512             {
   1513                 final int jumpOffsetLocation = out.size ();
   1514 
   1515                 // else write out zeros and submit for backpatching:
   1516                 out.write2 (0,
   1517                             0);
   1518 
   1519                 ctx.m_backpatchQueue.add (new int [] {2, jumpOffsetLocation, ip, targetBlockID});
   1520             }
   1521         }
   1522 
   1523         protected final void emitJumpOffset4 (final EmitCtx ctx, final int ip, final int targetBlockID)
   1524         {
   1525             final ByteArrayOStream out = ctx.m_out;
   1526 
   1527             if (targetBlockID <= m_parentBlockID)
   1528             {
   1529                 // backwards branch:
   1530                 final int jumpOffset = ((Block) ctx.m_blocks.m_blocks.get (targetBlockID)).m_first - ip;
   1531 
   1532                 out.write4 (jumpOffset >>> 24,    // targetbyte1
   1533                             jumpOffset >>> 16,    // targetbyte2
   1534                             jumpOffset >>> 8,     // targetbyte3
   1535                             jumpOffset);           // targetbyte4
   1536             }
   1537             else
   1538             {
   1539                 final int jumpOffsetLocation = out.size ();
   1540 
   1541                 // else write out zeros and submit for backpatching:
   1542                 out.write4 (0,
   1543                             0,
   1544                             0,
   1545                             0);
   1546 
   1547                 ctx.m_backpatchQueue.add (new int [] {4, jumpOffsetLocation, ip, targetBlockID});
   1548             }
   1549         }
   1550 
   1551         final byte m_opcode;
   1552         final int [] m_targets; // could be code offsets or block IDs
   1553 
   1554         int m_parentBlockID;
   1555 
   1556     } // end of nested class
   1557 
   1558 
   1559     // TODO: these could be static instance-pooled
   1560     static final class TERMINATE extends Branch // _[x]return, _athrow
   1561     {
   1562         TERMINATE (final int opcode)
   1563         {
   1564             super (opcode, null);
   1565         }
   1566 
   1567         int length () { return 1; }
   1568 
   1569         void emit (final EmitCtx ctx)
   1570         {
   1571             ctx.m_out.write (m_opcode);
   1572         }
   1573 
   1574     } // end of nested class
   1575 
   1576 
   1577     static final class RET extends Branch // [wide] ret
   1578     {
   1579         RET (final int opcode, final int varindex)
   1580         {
   1581             super (opcode, null);
   1582             m_varindex = varindex;
   1583         }
   1584 
   1585         int length () { return (m_varindex <= 0xFF) ? 2 : 3; }
   1586 
   1587         void emit (final EmitCtx ctx)
   1588         {
   1589             final ByteArrayOStream out = ctx.m_out;
   1590 
   1591             if (m_varindex <= 0xFF)
   1592             {
   1593                 out.write2 (m_opcode,
   1594                             m_varindex);  // indexbyte
   1595             }
   1596             else
   1597             {
   1598                 out.write4 (_wide,
   1599                             m_opcode,
   1600                             m_varindex >>> 8,   // indexbyte1
   1601                             m_varindex);         // indexbyte2
   1602             }
   1603         }
   1604 
   1605         final int m_varindex;
   1606 
   1607     } // end of nested class
   1608 
   1609 
   1610     static final class JUMP2 extends Branch // _goto, _jsr
   1611     {
   1612         JUMP2 (final int opcode, final int target)
   1613         {
   1614             super (opcode, new int [] {target});
   1615         }
   1616 
   1617         int maxlength () { return 5; }
   1618 
   1619         void emit (final EmitCtx ctx)
   1620         {
   1621             final ByteArrayOStream out = ctx.m_out;
   1622             final int targetBlockID = m_targets [0];
   1623             final int ip = out.size ();
   1624 
   1625             // TODO: switch to 4-byte long form if jump > 32k
   1626 
   1627             out.write (m_opcode);
   1628             emitJumpOffset2 (ctx, ip, targetBlockID);
   1629         }
   1630 
   1631     } // end of nested class
   1632 
   1633 
   1634     static final class JUMP4 extends Branch // _goto_w, _jsr_w
   1635     {
   1636         JUMP4 (final int opcode, final int target)
   1637         {
   1638             super (opcode, new int [] {target});
   1639         }
   1640 
   1641         int maxlength () { return 5; }
   1642 
   1643         void emit (final EmitCtx ctx)
   1644         {
   1645             final ByteArrayOStream out = ctx.m_out;
   1646             final int targetBlockID = m_targets [0];
   1647             final int ip = out.size ();
   1648 
   1649             out.write (m_opcode);
   1650             emitJumpOffset4 (ctx, ip, targetBlockID);
   1651         }
   1652 
   1653     } // end of nested class
   1654 
   1655 
   1656     static final class IFJUMP2 extends Branch // _ifxxx
   1657     {
   1658         IFJUMP2 (final int opcode, final int target)
   1659         {
   1660             super (opcode, new int [] {target});
   1661         }
   1662 
   1663         int maxlength () { return 8; }
   1664 
   1665         void emit (final EmitCtx ctx)
   1666         {
   1667             final ByteArrayOStream out = ctx.m_out;
   1668             final int targetBlockID = m_targets [0];
   1669             final int ip = out.size ();
   1670 
   1671             // TODO: switch to 8-byte long form if jump > 32k
   1672 
   1673             out.write (m_opcode);
   1674             emitJumpOffset2 (ctx, ip, targetBlockID);
   1675         }
   1676 
   1677     } // end of nested class
   1678 
   1679 
   1680     static final class LOOKUPSWITCH extends Branch
   1681     {
   1682         LOOKUPSWITCH (final int [] keys, final int [] targets /* first one is default */)
   1683         {
   1684             super (_lookupswitch, targets);
   1685             m_keys = keys;
   1686         }
   1687 
   1688         int maxlength () { return 12 + (m_keys.length << 3); }
   1689 
   1690         void emit (final EmitCtx ctx)
   1691         {
   1692             final ByteArrayOStream out = ctx.m_out;
   1693             final int ip = out.size ();
   1694 
   1695             out.write (m_opcode);
   1696 
   1697             // padding bytes:
   1698             for (int p = 0, padCount = 3 - (ip & 3); p < padCount; ++ p) out.write (0);
   1699 
   1700             // default:
   1701             emitJumpOffset4 (ctx, ip, m_targets [0]);
   1702 
   1703             // npairs count:
   1704             final int npairs = m_keys.length;
   1705             out.write4 (npairs >>> 24,  // byte1
   1706                         npairs >>> 16,  // byte2
   1707                         npairs >>> 8,   // byte3
   1708                         npairs);        // byte4
   1709 
   1710             // keyed targets:
   1711             for (int t = 1; t < m_targets.length; ++ t)
   1712             {
   1713                 final int key = m_keys [t - 1];
   1714                 out.write4 (key >>> 24,  // byte1
   1715                             key >>> 16,  // byte2
   1716                             key >>> 8,   // byte3
   1717                             key);         // byte4
   1718 
   1719                 // key target:
   1720                 emitJumpOffset4 (ctx, ip, m_targets [t]);
   1721             }
   1722         }
   1723 
   1724         final int [] m_keys;
   1725 
   1726     } // end of nested class
   1727 
   1728 
   1729     static final class TABLESWITCH extends Branch
   1730     {
   1731         TABLESWITCH (final int low, final int high, final int [] targets /* first one is default */)
   1732         {
   1733             super (_tableswitch, targets);
   1734             m_low = low;
   1735             m_high = high;
   1736         }
   1737 
   1738         int maxlength () { return 12 + (m_targets.length << 2); }
   1739 
   1740         void emit (final EmitCtx ctx)
   1741         {
   1742             final ByteArrayOStream out = ctx.m_out;
   1743             final int ip = out.size ();
   1744 
   1745             // TODO: switch to long form for any jump > 32k
   1746 
   1747             out.write (m_opcode);
   1748 
   1749             // padding bytes:
   1750             for (int p = 0, padCount = 3 - (ip & 3); p < padCount; ++ p) out.write (0);
   1751 
   1752             // default:
   1753             emitJumpOffset4 (ctx, ip, m_targets [0]);
   1754 
   1755             // low, high:
   1756             final int low = m_low;
   1757             out.write4 (low >>> 24,  // byte1
   1758                         low >>> 16,  // byte2
   1759                         low >>> 8,   // byte3
   1760                         low);        // byte4
   1761 
   1762             final int high = m_high;
   1763             out.write4 (high >>> 24,  // byte1
   1764                         high >>> 16,  // byte2
   1765                         high >>> 8,   // byte3
   1766                         high);        // byte4
   1767 
   1768             // targets:
   1769             for (int t = 1; t < m_targets.length; ++ t)
   1770             {
   1771                 // key target:
   1772                 emitJumpOffset4 (ctx, ip, m_targets [t]);
   1773             }
   1774         }
   1775 
   1776         final int m_low, m_high;
   1777 
   1778     } // end of nested class
   1779 
   1780 
   1781     /**
   1782      * TODO: CodeSegment right now must be 100% position-independent code;
   1783      * otherwise it must follow maxlengtt() Branch pattern...
   1784      */
   1785     static abstract class CodeSegment
   1786     {
   1787         CodeSegment (final InstrVisitor visitor)
   1788         {
   1789             m_visitor = visitor; // TODO: will this field be used?
   1790         }
   1791 
   1792         abstract int length ();
   1793         abstract int maxstack ();
   1794         abstract void emit (EmitCtx ctx);
   1795 
   1796 
   1797         final InstrVisitor m_visitor;
   1798 
   1799     } // end of nested class
   1800 
   1801 
   1802     static final class clinitHeader extends CodeSegment
   1803     {
   1804         clinitHeader (final InstrVisitor visitor, final int localVarIndex)
   1805         {
   1806             super (visitor);
   1807             final ByteArrayOStream buf = new ByteArrayOStream (CLINIT_HEADER_INIT_CAPACITY);
   1808             m_buf = buf;
   1809 
   1810             final ClassDef cls = visitor.m_cls;
   1811 
   1812             final int [] blockCounts = visitor.m_classBlockCounts;
   1813             final int instrMethodCount = visitor.m_classInstrMethodCount; // actual number of methods to instrument may be less than the size of the block map
   1814             if ($assert.ENABLED) $assert.ASSERT (blockCounts != null && blockCounts.length >= instrMethodCount,
   1815                 "invalid block count map");
   1816 
   1817             final int coverageFieldrefIndex = visitor.m_coverageFieldrefIndex;
   1818             final int preclinitMethodrefIndex = visitor.m_preclinitMethodrefIndex;
   1819             final int classNameConstantIndex = visitor.m_classNameConstantIndex;
   1820 
   1821             if ($assert.ENABLED)
   1822             {
   1823                 $assert.ASSERT (coverageFieldrefIndex > 0, "invalid coverageFieldrefIndex");
   1824                 $assert.ASSERT (preclinitMethodrefIndex > 0, "invalid registerMethodrefIndex");
   1825                 $assert.ASSERT (classNameConstantIndex > 0, "invalid classNameConstantIndex");
   1826             }
   1827 
   1828             // init and load COVERAGE_FIELD:
   1829             buf.write3 (_invokestatic,
   1830                         preclinitMethodrefIndex >>> 8,    // indexbyte1
   1831                         preclinitMethodrefIndex);         // indexbyte2
   1832 
   1833             // [stack +1]
   1834 
   1835             // TODO: disable this when there are no real blocks following?
   1836             // [in general, use a different template when this method contains a single block]
   1837 
   1838             // TODO: if this method has been added by us, do not instrument its blocks
   1839 
   1840             // push int literal equal to 'methodID' [for the parent method]:
   1841             CodeGen.push_int_value (buf, cls, visitor.m_methodID);
   1842 
   1843             // [stack +2]
   1844 
   1845             // push subarray reference:
   1846             buf.write (_aaload);
   1847 
   1848             // [stack +1]
   1849 
   1850             // store it in alias var:
   1851             CodeGen.store_local_object_var (buf, localVarIndex);
   1852 
   1853             // [stack +0]
   1854         }
   1855 
   1856         int length () { return m_buf.size (); }
   1857         int maxstack () { return 2; } // note: needs to be updated each time emitted code changes
   1858 
   1859         void emit (final EmitCtx ctx)
   1860         {
   1861             // TODO: better error handling here?
   1862             try
   1863             {
   1864                 m_buf.writeTo (ctx.m_out);
   1865             }
   1866             catch (IOException ioe)
   1867             {
   1868                 if ($assert.ENABLED) $assert.ASSERT (false, ioe.toString ());
   1869             }
   1870         }
   1871 
   1872 
   1873         private final ByteArrayOStream m_buf;
   1874 
   1875         private static final int CLINIT_HEADER_INIT_CAPACITY = 32; // covers about 80% of classes (no reallocation)
   1876 
   1877     } // end of nested class
   1878 
   1879 
   1880     static final class methodHeader extends CodeSegment
   1881     {
   1882         methodHeader (final InstrVisitor visitor, final int localVarIndex)
   1883         {
   1884             super (visitor);
   1885             final ByteArrayOStream buf = new ByteArrayOStream (HEADER_INIT_CAPACITY);
   1886             m_buf = buf;
   1887 
   1888             final ClassDef cls = visitor.m_cls;
   1889             final int coverageFieldrefIndex = visitor.m_coverageFieldrefIndex;
   1890             final int preclinitMethodrefIndex = visitor.m_preclinitMethodrefIndex;
   1891 
   1892             // TODO: disable this when there are no real blocks following?
   1893             // [in general, use a different template when this method contains a single block]
   1894 
   1895             // push ref to the static field and dup it:
   1896             buf.write4 (_getstatic,
   1897                         coverageFieldrefIndex >>> 8, // indexbyte1
   1898                         coverageFieldrefIndex,       // indexbyte2
   1899                         _dup);
   1900 
   1901             // [stack +2]
   1902 
   1903             // SF FR 971186: check if it is null and if so run the field
   1904             // init and class RT register code (only relevant for
   1905             // methods that can be executed ahead of <clinit>) [rare]
   1906 
   1907             buf.write3 (_ifnonnull, // skip over pre-<clinit> method call
   1908                         0,
   1909                         3 + /* size of the block below */ 4);
   1910 
   1911             // [stack +1]
   1912 
   1913             // block: call pre-<clinit> method
   1914             {
   1915                 buf.write4 (_pop,
   1916                             _invokestatic,
   1917                             preclinitMethodrefIndex >>> 8,    // indexbyte1
   1918                             preclinitMethodrefIndex);         // indexbyte2
   1919 
   1920                 // [stack +1]
   1921             }
   1922 
   1923             // push int literal equal to 'methodID':
   1924             CodeGen.push_int_value (buf, cls, visitor.m_methodID);
   1925 
   1926             // [stack +2]
   1927 
   1928             // push subarray reference:
   1929             buf.write (_aaload);
   1930 
   1931             // [stack +1]
   1932 
   1933             // store it in alias var:
   1934             CodeGen.store_local_object_var (buf, localVarIndex);
   1935 
   1936             // [stack +0]
   1937         }
   1938 
   1939         int length () { return m_buf.size (); }
   1940         int maxstack () { return 2; } // note: needs to be updated each time emitted code changes
   1941 
   1942         void emit (final EmitCtx ctx)
   1943         {
   1944             // TODO: better error handling here?
   1945             try
   1946             {
   1947                 m_buf.writeTo (ctx.m_out);
   1948             }
   1949             catch (IOException ioe)
   1950             {
   1951                 if ($assert.ENABLED) $assert.ASSERT (false, ioe.toString ());
   1952             }
   1953         }
   1954 
   1955 
   1956         private final ByteArrayOStream m_buf;
   1957 
   1958         private static final int HEADER_INIT_CAPACITY = 16;
   1959 
   1960     } // end of nested class
   1961 
   1962 
   1963     static final class BlockSegment extends CodeSegment
   1964     {
   1965         public BlockSegment (final InstrVisitor visitor, final int localVarIndex, final int blockID)
   1966         {
   1967             super (visitor);
   1968             final ByteArrayOStream buf = new ByteArrayOStream (BLOCK_INIT_CAPACITY);
   1969             m_buf = buf;
   1970 
   1971             final ClassDef cls = visitor.m_cls;
   1972 
   1973             // push alias var:
   1974             CodeGen.load_local_object_var (buf, localVarIndex);
   1975 
   1976             // [stack +1]
   1977 
   1978             // push int value equal to 'blockID':
   1979             CodeGen.push_int_value (buf, cls, blockID);
   1980 
   1981             // [stack +2]
   1982 
   1983             // push boolean 'true':
   1984             buf.write2 (_iconst_1,
   1985 
   1986             // [stack +3]
   1987 
   1988             // store it in the array:
   1989                         _bastore);
   1990 
   1991             // [stack +0]
   1992         }
   1993 
   1994         int length () { return m_buf.size (); }
   1995         int maxstack () { return 3; } // note: needs to be updated each time emitted code changes
   1996 
   1997         void emit (final EmitCtx ctx)
   1998         {
   1999             // TODO: better error handling here?
   2000             try
   2001             {
   2002                 m_buf.writeTo (ctx.m_out);
   2003             }
   2004             catch (IOException ioe)
   2005             {
   2006                 if ($assert.ENABLED) $assert.ASSERT (false, ioe.toString ());
   2007             }
   2008         }
   2009 
   2010 
   2011         private final ByteArrayOStream m_buf;
   2012 
   2013         private static final int BLOCK_INIT_CAPACITY = 16;
   2014 
   2015     } // end of nested class
   2016 
   2017 
   2018     private static final class LineNumberComparator implements Comparator
   2019     {
   2020         public final int compare (final Object o1, final Object o2)
   2021         {
   2022             return ((LineNumber_info) o1).m_start_pc - ((LineNumber_info) o2).m_start_pc;
   2023         }
   2024 
   2025     } // end of nested class
   2026 
   2027 
   2028 
   2029     private void setClassName (final String fullName)
   2030     {
   2031         if ($assert.ENABLED) $assert.ASSERT (fullName != null && fullName.length () > 0,
   2032             "null or empty input: fullName");
   2033 
   2034         final int lastSlash = fullName.lastIndexOf ('/');
   2035         if (lastSlash < 0)
   2036         {
   2037             m_classPackageName = "";
   2038             m_className = fullName;
   2039         }
   2040         else
   2041         {
   2042             if ($assert.ENABLED) $assert.ASSERT (lastSlash < fullName.length () - 1,
   2043                 "malformed class name [" + fullName + "]");
   2044 
   2045             m_classPackageName = fullName.substring (0, lastSlash);
   2046             m_className = fullName.substring (lastSlash + 1);
   2047         }
   2048     }
   2049 
   2050     private void consumeSignatureData (final int methodID, final int [] basicBlockOffsets)
   2051     {
   2052         // note: by itself, this is not a very good checksum for a class def;
   2053         // however, it is fast to compute and since it will be used along with
   2054         // a class name it should be good at detecting structural changes that
   2055         // matter to us (method and basic block ordering/sizes)
   2056 
   2057         final int temp1 = basicBlockOffsets.length;
   2058         long temp2 = NBEAST * m_classSignature + (methodID + 1) * temp1;
   2059 
   2060         for (int i = 1; i < temp1; ++ i) // skip the initial 0 offset
   2061         {
   2062             temp2 = NBEAST * temp2 + basicBlockOffsets [i];
   2063         }
   2064 
   2065         m_classSignature = temp2;
   2066     }
   2067 
   2068     // TODO: use a compilation flag to use table assist here instead of binary search
   2069     // BETTER YET: use binsearch for online mode and table assist for offline [when memory is not an issue]
   2070 
   2071     /**
   2072      * Returns the maximum index 'i' such that (values[i] <= x). values[]
   2073      * contains distinct non-negative integers in increasing order. values[0] is 0,
   2074      * 'x' is non-negative.
   2075      *
   2076      * Edge case:
   2077      *  returns values.length-1 if values [values.length - 1] < x
   2078      */
   2079     private static int lowbound (final int [] values, final int x)
   2080     {
   2081         int low = 0, high = values.length - 1;
   2082 
   2083         // assertion: lb is in [low, high]
   2084 
   2085         while (low <= high)
   2086         {
   2087             final int m = (low + high) >> 1;
   2088             final int v = values [m];
   2089 
   2090             if (v == x)
   2091                 return m;
   2092             else if (v < x)
   2093                 low = m + 1;
   2094             else // v > x
   2095                 high = m - 1;
   2096         }
   2097 
   2098         return high;
   2099     }
   2100 
   2101     private void reset ()
   2102     {
   2103         // TODO: check that all state is reset
   2104 
   2105         m_instrument = false;
   2106         m_metadata = false;
   2107         m_ignoreAlreadyInstrumented = false;
   2108 
   2109         m_cls = null;
   2110         m_classPackageName = null;
   2111         m_className = null;
   2112         m_classSrcFileName = null;
   2113         m_classBlockMetadata = null;
   2114         m_classMethodDescriptors = null;
   2115 
   2116         m_syntheticStringIndex = -1;
   2117         m_coverageFieldrefIndex = -1;
   2118         m_registerMethodrefIndex = -1;
   2119         m_preclinitMethodrefIndex = -1;
   2120         m_classNameConstantIndex = -1;
   2121         m_clinitID = -1;
   2122         m_clinitStatus = 0;
   2123         m_classInstrMethodCount = -1;
   2124         m_classBlockCounts = null;
   2125         m_classSignature = 0;
   2126 
   2127         m_methodID = -1;
   2128         m_methodName = null;
   2129         m_methodFirstLine = 0;
   2130         m_methodBlockOffsets = null;
   2131         m_methodJumpAdjOffsets = null;
   2132         m_methodJumpAdjValues = null;
   2133     }
   2134 
   2135 
   2136     private final boolean m_excludeSyntheticMethods;
   2137     private final boolean m_excludeBridgeMethods;
   2138     private final boolean m_doSUIDCompensation;
   2139 
   2140     private final Logger m_log; // instr visitor logging context is latched at construction time
   2141 
   2142     // non-resettable state:
   2143 
   2144     private boolean m_warningIssued;
   2145 
   2146 
   2147     // resettable state:
   2148 
   2149     private boolean m_instrument;
   2150     private boolean m_metadata;
   2151     private boolean m_ignoreAlreadyInstrumented;
   2152 
   2153     /*private*/ ClassDef m_cls;
   2154     private String m_classPackageName; // in JVM format [com/vladium/...]; empty string for default package
   2155     private String m_className; // in JVM format [<init>, <clinit>, etc], relative to 'm_classPackageName'
   2156     private String m_classSrcFileName;
   2157     private int [][][] m_classBlockMetadata; // methodID->(blockID->line) map [valid only if 'm_constructMetadata' is true; null if the method has not line number table]
   2158     private MethodDescriptor [] m_classMethodDescriptors;
   2159 
   2160     // current class scope:
   2161     private int m_syntheticStringIndex;     // index of CONSTANT_Utf8 String that reads "Synthetic"
   2162     /*private*/ int m_coverageFieldrefIndex;    // index of the Fieldref for COVERAGE_FIELD
   2163     private int m_registerMethodrefIndex;   // index of Methodref for RT.r()
   2164     /*private*/ int m_preclinitMethodrefIndex;  // index of Methodref for pre-<clinit> method
   2165     /*private*/ int m_classNameConstantIndex;   // index of CONSTANT_String that is the class name [in JVM format]
   2166     private int m_stampIndex;               // index of CONSTANT_Long that is the class instr stamp
   2167     private int m_clinitID;                 // offset of <clinit> method [-1 if not determined yet]
   2168     private int m_clinitStatus;
   2169     /*private*/ int m_classInstrMethodCount;    // the number of slots in 'm_classBlockCounts' corresponding to methods to be instrumented for coverage
   2170     /*private*/ int [] m_classBlockCounts;      // basic block counts for all methods [only valid just before <clinit> is processed]
   2171     private long m_classSignature;
   2172 
   2173     // current method scope:
   2174     /*private*/ int m_methodID;                 // offset of current method being instrumented
   2175     private String m_methodName;
   2176     private int m_methodFirstLine;
   2177     private int [] m_methodBlockOffsets;    // [unadjusted] basic block boundaries [length = m_classBlockCounts[m_methodID]+1; the last slot is method bytecode length]
   2178     private int [] m_methodBlockSizes;
   2179     private int [] m_methodJumpAdjOffsets;    // TODO: length ?
   2180     private int [] m_methodJumpAdjValues;        // TODO: length ?
   2181 
   2182 
   2183     private static final long NBEAST = 16661; // prime
   2184 
   2185     private static final String COVERAGE_FIELD_NAME = "$VR" + "c";
   2186     private static final String SUID_FIELD_NAME = "serialVersionUID";
   2187     private static final String PRECLINIT_METHOD_NAME = "$VR" + "i";
   2188 
   2189     private static final String JAVA_IO_SERIALIZABLE_NAME = "java/io/Serializable";
   2190     private static final String JAVA_IO_EXTERNALIZABLE_NAME = "java/io/Externalizable";
   2191 
   2192     private static final int EMIT_CTX_MIN_INIT_CAPACITY = 64; // good value determined empirically
   2193     private static final int PRECLINIT_INIT_CAPACITY = 128; // covers about 80% of classes (no reallocation)
   2194     private static final boolean MARK_ADDED_ELEMENTS_SYNTHETIC = true;
   2195 
   2196     /* It appears that nested classes and interfaces ought to be marked
   2197      * as Synthetic; however, neither Sun nor IBM compilers seem to do this.
   2198      *
   2199      * (As a side note, implied no-arg constructors ought to be marked as
   2200      * synthetic as well, but Sun's javac is not consistent about that either)
   2201      */
   2202     private static final boolean SKIP_SYNTHETIC_CLASSES = false;
   2203 
   2204     private static final LineNumberComparator LINE_NUMBER_COMPARATOR = new LineNumberComparator ();
   2205 
   2206     private static final byte [] EMPTY_BYTE_ARRAY = new byte [0];
   2207 
   2208 } // end of class
   2209 // ----------------------------------------------------------------------------