KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > vladium > emma > instr > InstrVisitor


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 JavaDoc;
12 import java.util.ArrayList JavaDoc;
13 import java.util.Arrays JavaDoc;
14 import java.util.Comparator JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.List JavaDoc;
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 JavaDoc visit (final ClassDef ignore, final Object JavaDoc ctx)
128     {
129         final ClassDef cls = m_cls;
130         final String JavaDoc clsVMName = cls.getName ();
131         final String JavaDoc 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 JavaDoc ("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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc visit (final IMethodCollection methods, final Object JavaDoc 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 JavaDoc visit (final IAttributeCollection attributes, Object JavaDoc 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 JavaDoc visit (final CodeAttribute_info attribute, final Object JavaDoc 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 JavaDoc s = new StringBuffer JavaDoc ("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 JavaDoc 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 JavaDoc visit (final LineNumberTableAttribute_info attribute, final Object JavaDoc 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 JavaDoc s = new StringBuffer JavaDoc ("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 JavaDoc visit (final ExceptionsAttribute_info attribute, final Object JavaDoc ctx)
1354    {
1355        return ctx;
1356    }
1357    
1358    public Object JavaDoc visit (final ConstantValueAttribute_info attribute, final Object JavaDoc ctx)
1359    {
1360        return ctx;
1361    }
1362    
1363    public Object JavaDoc visit (final SourceFileAttribute_info attribute, final Object JavaDoc ctx)
1364    {
1365        m_classSrcFileName = attribute.getSourceFile (m_cls).m_value;
1366
1367        return ctx;
1368    }
1369
1370    public Object JavaDoc visit (final SyntheticAttribute_info attribute, final Object JavaDoc ctx)
1371    {
1372        return ctx;
1373    }
1374    
1375    public Object JavaDoc visit (final BridgeAttribute_info attribute, final Object JavaDoc ctx)
1376    {
1377        return ctx;
1378    }
1379    
1380    public Object JavaDoc visit (final InnerClassesAttribute_info attribute, final Object JavaDoc ctx)
1381    {
1382        return ctx;
1383    }
1384    
1385    public Object JavaDoc visit (final GenericAttribute_info attribute, final Object JavaDoc 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 JavaDoc ();
1402        }
1403        
1404        BlockList (final int capacity)
1405        {
1406            m_blocks = new ArrayList JavaDoc (capacity);
1407        }
1408        
1409        final List JavaDoc /* 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 JavaDoc ();
1471        }
1472        
1473        final BlockList m_blocks;
1474        final ByteArrayOStream m_out;
1475        final List JavaDoc /* 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc
2019    {
2020        public final int compare (final Object JavaDoc o1, final Object JavaDoc 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 JavaDoc 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 JavaDoc m_classPackageName; // in JVM format [com/vladium/...]; empty string for default package
2155
private String JavaDoc m_className; // in JVM format [<init>, <clinit>, etc], relative to 'm_classPackageName'
2156
private String JavaDoc 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 JavaDoc 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 JavaDoc COVERAGE_FIELD_NAME = "$VR" + "c";
2186    private static final String JavaDoc SUID_FIELD_NAME = "serialVersionUID";
2187    private static final String JavaDoc PRECLINIT_METHOD_NAME = "$VR" + "i";
2188
2189    private static final String JavaDoc JAVA_IO_SERIALIZABLE_NAME = "java/io/Serializable";
2190    private static final String JavaDoc 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
// ----------------------------------------------------------------------------
Popular Tags