• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
InstrVisitor(final CoverageOptions options)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      */
process(final ClassDef cls, final boolean ignoreAlreadyInstrumented, final boolean instrument, final boolean metadata, final InstrResult out)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 
visit(final ClassDef ignore, final Object ctx)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 
visit(final IMethodCollection methods, final Object ctx)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 
visit(final IAttributeCollection attributes, Object ctx)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 
visit(final CodeAttribute_info attribute, final Object ctx)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 
visit(final LineNumberTableAttribute_info attribute, final Object ctx)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 
visit(final ExceptionsAttribute_info attribute, final Object ctx)1353     public Object visit (final ExceptionsAttribute_info attribute, final Object ctx)
1354     {
1355         return ctx;
1356     }
1357 
visit(final ConstantValueAttribute_info attribute, final Object ctx)1358     public Object visit (final ConstantValueAttribute_info attribute, final Object ctx)
1359     {
1360         return ctx;
1361     }
1362 
visit(final SourceFileAttribute_info attribute, final Object ctx)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 
visit(final SyntheticAttribute_info attribute, final Object ctx)1370     public Object visit (final SyntheticAttribute_info attribute, final Object ctx)
1371     {
1372         return ctx;
1373     }
1374 
visit(final BridgeAttribute_info attribute, final Object ctx)1375     public Object visit (final BridgeAttribute_info attribute, final Object ctx)
1376     {
1377         return ctx;
1378     }
1379 
visit(final InnerClassesAttribute_info attribute, final Object ctx)1380     public Object visit (final InnerClassesAttribute_info attribute, final Object ctx)
1381     {
1382         return ctx;
1383     }
1384 
visit(final GenericAttribute_info attribute, final Object ctx)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     {
BlockList()1399         BlockList ()
1400         {
1401             m_blocks = new ArrayList ();
1402         }
1403 
BlockList(final int capacity)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          */
emit(final EmitCtx ctx, final byte [] code)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 
EmitCtx(final BlockList blocks, final ByteArrayOStream out)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     {
Branch(final int opcode, final int [] targets)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          */
maxlength()1494         int maxlength () { return 1; }
1495 
emit(EmitCtx ctx)1496         abstract void emit (EmitCtx ctx);
1497 
1498         // TODO: this method must signal when it is necessary to switch to long jump form
emitJumpOffset2(final EmitCtx ctx, final int ip, final int targetBlockID)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 
emitJumpOffset4(final EmitCtx ctx, final int ip, final int targetBlockID)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     {
TERMINATE(final int opcode)1562         TERMINATE (final int opcode)
1563         {
1564             super (opcode, null);
1565         }
1566 
length()1567         int length () { return 1; }
1568 
emit(final EmitCtx ctx)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     {
RET(final int opcode, final int varindex)1579         RET (final int opcode, final int varindex)
1580         {
1581             super (opcode, null);
1582             m_varindex = varindex;
1583         }
1584 
length()1585         int length () { return (m_varindex <= 0xFF) ? 2 : 3; }
1586 
emit(final EmitCtx ctx)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     {
JUMP2(final int opcode, final int target)1612         JUMP2 (final int opcode, final int target)
1613         {
1614             super (opcode, new int [] {target});
1615         }
1616 
maxlength()1617         int maxlength () { return 5; }
1618 
emit(final EmitCtx ctx)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     {
JUMP4(final int opcode, final int target)1636         JUMP4 (final int opcode, final int target)
1637         {
1638             super (opcode, new int [] {target});
1639         }
1640 
maxlength()1641         int maxlength () { return 5; }
1642 
emit(final EmitCtx ctx)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     {
IFJUMP2(final int opcode, final int target)1658         IFJUMP2 (final int opcode, final int target)
1659         {
1660             super (opcode, new int [] {target});
1661         }
1662 
maxlength()1663         int maxlength () { return 8; }
1664 
emit(final EmitCtx ctx)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     {
LOOKUPSWITCH(final int [] keys, final int [] targets )1682         LOOKUPSWITCH (final int [] keys, final int [] targets /* first one is default */)
1683         {
1684             super (_lookupswitch, targets);
1685             m_keys = keys;
1686         }
1687 
maxlength()1688         int maxlength () { return 12 + (m_keys.length << 3); }
1689 
emit(final EmitCtx ctx)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     {
TABLESWITCH(final int low, final int high, final int [] targets )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 
maxlength()1738         int maxlength () { return 12 + (m_targets.length << 2); }
1739 
emit(final EmitCtx ctx)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     {
CodeSegment(final InstrVisitor visitor)1787         CodeSegment (final InstrVisitor visitor)
1788         {
1789             m_visitor = visitor; // TODO: will this field be used?
1790         }
1791 
length()1792         abstract int length ();
maxstack()1793         abstract int maxstack ();
emit(EmitCtx ctx)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     {
clinitHeader(final InstrVisitor visitor, final int localVarIndex)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 
length()1856         int length () { return m_buf.size (); }
maxstack()1857         int maxstack () { return 2; } // note: needs to be updated each time emitted code changes
1858 
emit(final EmitCtx ctx)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     {
methodHeader(final InstrVisitor visitor, final int localVarIndex)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 
length()1939         int length () { return m_buf.size (); }
maxstack()1940         int maxstack () { return 2; } // note: needs to be updated each time emitted code changes
1941 
emit(final EmitCtx ctx)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     {
BlockSegment(final InstrVisitor visitor, final int localVarIndex, final int blockID)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 
length()1994         int length () { return m_buf.size (); }
maxstack()1995         int maxstack () { return 3; } // note: needs to be updated each time emitted code changes
1996 
emit(final EmitCtx ctx)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     {
compare(final Object o1, final Object o2)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 
setClassName(final String fullName)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 // ----------------------------------------------------------------------------