View Javadoc
1   /*
2    * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.tools.asm;
27  
28  import sun.tools.java.*;
29  import java.util.Enumeration;
30  import java.io.IOException;
31  import java.io.DataOutputStream;
32  import java.io.PrintStream;
33  import java.util.Vector;
34  // JCOV
35  import sun.tools.javac.*;
36  import java.io.File;
37  import java.io.BufferedInputStream;
38  import java.io.DataInputStream;
39  import java.io.FileInputStream;
40  import java.io.FileNotFoundException;
41  import java.io.FileOutputStream;
42  import java.lang.String;
43  // end JCOV
44  
45  /**
46   * This class is used to assemble the bytecode instructions for a method.
47   *
48   * WARNING: The contents of this source file are not part of any
49   * supported API.  Code that depends on them does so at its own risk:
50   * they are subject to change or removal without notice.
51   *
52   * @author Arthur van Hoff
53   */
54  public final
55  class Assembler implements Constants {
56      static final int NOTREACHED         = 0;
57      static final int REACHED            = 1;
58      static final int NEEDED             = 2;
59  
60      Label first = new Label();
61      Instruction last = first;
62      int maxdepth;
63      int maxvar;
64      int maxpc;
65  
66      /**
67       * Add an instruction
68       */
69      public void add(Instruction inst) {
70          if (inst != null) {
71              last.next = inst;
72              last = inst;
73          }
74      }
75      public void add(long where, int opc) {
76          add(new Instruction(where, opc, null));
77      }
78      public void add(long where, int opc, Object obj) {
79          add(new Instruction(where, opc, obj));
80      }
81  // JCOV
82      public void add(long where, int opc, Object obj, boolean flagCondInverted) {
83          add(new Instruction(where, opc, obj, flagCondInverted));
84      }
85  
86      public void add(boolean flagNoCovered, long where, int opc, Object obj) {
87          add(new Instruction(flagNoCovered, where, opc, obj));
88      }
89  
90      public void add(long where, int opc, boolean flagNoCovered) {
91          add(new Instruction(where, opc, flagNoCovered));
92      }
93  
94      static Vector<String> SourceClassList = new Vector<>();
95  
96      static Vector<String> TmpCovTable = new Vector<>();
97  
98      static int[]  JcovClassCountArray = new int[CT_LAST_KIND + 1];
99  
100     static String JcovMagicLine     = "JCOV-DATA-FILE-VERSION: 2.0";
101     static String JcovClassLine     = "CLASS: ";
102     static String JcovSrcfileLine   = "SRCFILE: ";
103     static String JcovTimestampLine = "TIMESTAMP: ";
104     static String JcovDataLine      = "DATA: ";
105     static String JcovHeadingLine   = "#kind\tcount";
106 
107     static int[]  arrayModifiers    =
108                 {M_PUBLIC, M_PRIVATE, M_PROTECTED, M_ABSTRACT, M_FINAL, M_INTERFACE};
109     static int[]  arrayModifiersOpc =
110                 {PUBLIC, PRIVATE, PROTECTED, ABSTRACT, FINAL, INTERFACE};
111 //end JCOV
112 
113     /**
114      * Optimize instructions and mark those that can be reached
115      */
116     void optimize(Environment env, Label lbl) {
117         lbl.pc = REACHED;
118 
119         for (Instruction inst = lbl.next ; inst != null ; inst = inst.next)  {
120             switch (inst.pc) {
121               case NOTREACHED:
122                 inst.optimize(env);
123                 inst.pc = REACHED;
124                 break;
125               case REACHED:
126                 return;
127               case NEEDED:
128                 break;
129             }
130 
131             switch (inst.opc) {
132               case opc_label:
133               case opc_dead:
134                 if (inst.pc == REACHED) {
135                     inst.pc = NOTREACHED;
136                 }
137                 break;
138 
139               case opc_ifeq:
140               case opc_ifne:
141               case opc_ifgt:
142               case opc_ifge:
143               case opc_iflt:
144               case opc_ifle:
145               case opc_if_icmpeq:
146               case opc_if_icmpne:
147               case opc_if_icmpgt:
148               case opc_if_icmpge:
149               case opc_if_icmplt:
150               case opc_if_icmple:
151               case opc_if_acmpeq:
152               case opc_if_acmpne:
153               case opc_ifnull:
154               case opc_ifnonnull:
155                 optimize(env, (Label)inst.value);
156                 break;
157 
158               case opc_goto:
159                 optimize(env, (Label)inst.value);
160                 return;
161 
162               case opc_jsr:
163                 optimize(env, (Label)inst.value);
164                 break;
165 
166               case opc_ret:
167               case opc_return:
168               case opc_ireturn:
169               case opc_lreturn:
170               case opc_freturn:
171               case opc_dreturn:
172               case opc_areturn:
173               case opc_athrow:
174                 return;
175 
176               case opc_tableswitch:
177               case opc_lookupswitch: {
178                 SwitchData sw = (SwitchData)inst.value;
179                 optimize(env, sw.defaultLabel);
180                 for (Enumeration<Label> e = sw.tab.elements() ; e.hasMoreElements();) {
181                     optimize(env, e.nextElement());
182                 }
183                 return;
184               }
185 
186               case opc_try: {
187                 TryData td = (TryData)inst.value;
188                 td.getEndLabel().pc = NEEDED;
189                 for (Enumeration<CatchData> e = td.catches.elements() ; e.hasMoreElements();) {
190                     CatchData cd = e.nextElement();
191                     optimize(env, cd.getLabel());
192                 }
193                 break;
194               }
195             }
196         }
197     }
198 
199     /**
200      * Eliminate instructions that are not reached
201      */
202     boolean eliminate() {
203         boolean change = false;
204         Instruction prev = first;
205 
206         for (Instruction inst = first.next ; inst != null ; inst = inst.next) {
207             if (inst.pc != NOTREACHED) {
208                 prev.next = inst;
209                 prev = inst;
210                 inst.pc = NOTREACHED;
211             } else {
212                 change = true;
213             }
214         }
215         first.pc = NOTREACHED;
216         prev.next = null;
217         return change;
218     }
219 
220     /**
221      * Optimize the byte codes
222      */
223     public void optimize(Environment env) {
224         //listing(System.out);
225         do {
226             // Figure out which instructions are reached
227             optimize(env, first);
228 
229             // Eliminate instructions that are not reached
230         } while (eliminate() && env.opt());
231     }
232 
233     /**
234      * Collect all constants into the constant table
235      */
236     public void collect(Environment env, MemberDefinition field, ConstantPool tab) {
237         // Collect constants for arguments only
238         // if a local variable table is generated
239         if ((field != null) && env.debug_vars()) {
240             @SuppressWarnings("unchecked")
241             Vector<MemberDefinition> v = (Vector<MemberDefinition>)field.getArguments();
242             if (v != null) {
243                 for (Enumeration<MemberDefinition> e = v.elements() ; e.hasMoreElements() ;) {
244                     MemberDefinition f = e.nextElement();
245                     tab.put(f.getName().toString());
246                     tab.put(f.getType().getTypeSignature());
247                 }
248             }
249         }
250 
251         // Collect constants from the instructions
252         for (Instruction inst = first ; inst != null ; inst = inst.next) {
253             inst.collect(tab);
254         }
255     }
256 
257     /**
258      * Determine stack size, count local variables
259      */
260     void balance(Label lbl, int depth) {
261         for (Instruction inst = lbl ; inst != null ; inst = inst.next)  {
262             //Environment.debugOutput(inst.toString() + ": " + depth + " => " +
263             //                                 (depth + inst.balance()));
264             depth += inst.balance();
265             if (depth < 0) {
266                throw new CompilerError("stack under flow: " + inst.toString() + " = " + depth);
267             }
268             if (depth > maxdepth) {
269                 maxdepth = depth;
270             }
271             switch (inst.opc) {
272               case opc_label:
273                 lbl = (Label)inst;
274                 if (inst.pc == REACHED) {
275                     if (lbl.depth != depth) {
276                         throw new CompilerError("stack depth error " +
277                                                 depth + "/" + lbl.depth +
278                                                 ": " + inst.toString());
279                     }
280                     return;
281                 }
282                 lbl.pc = REACHED;
283                 lbl.depth = depth;
284                 break;
285 
286               case opc_ifeq:
287               case opc_ifne:
288               case opc_ifgt:
289               case opc_ifge:
290               case opc_iflt:
291               case opc_ifle:
292               case opc_if_icmpeq:
293               case opc_if_icmpne:
294               case opc_if_icmpgt:
295               case opc_if_icmpge:
296               case opc_if_icmplt:
297               case opc_if_icmple:
298               case opc_if_acmpeq:
299               case opc_if_acmpne:
300               case opc_ifnull:
301               case opc_ifnonnull:
302                 balance((Label)inst.value, depth);
303                 break;
304 
305               case opc_goto:
306                 balance((Label)inst.value, depth);
307                 return;
308 
309               case opc_jsr:
310                 balance((Label)inst.value, depth + 1);
311                 break;
312 
313               case opc_ret:
314               case opc_return:
315               case opc_ireturn:
316               case opc_lreturn:
317               case opc_freturn:
318               case opc_dreturn:
319               case opc_areturn:
320               case opc_athrow:
321                 return;
322 
323               case opc_iload:
324               case opc_fload:
325               case opc_aload:
326               case opc_istore:
327               case opc_fstore:
328               case opc_astore: {
329                 int v = ((inst.value instanceof Number)
330                             ? ((Number)inst.value).intValue()
331                             : ((LocalVariable)inst.value).slot) + 1;
332                 if (v > maxvar)
333                     maxvar = v;
334                 break;
335               }
336 
337               case opc_lload:
338               case opc_dload:
339               case opc_lstore:
340               case opc_dstore: {
341                 int v = ((inst.value instanceof Number)
342                             ? ((Number)inst.value).intValue()
343                             : ((LocalVariable)inst.value).slot) + 2;
344                 if (v  > maxvar)
345                     maxvar = v;
346                 break;
347               }
348 
349               case opc_iinc: {
350                   int v = ((int[])inst.value)[0] + 1;
351                   if (v  > maxvar)
352                       maxvar = v + 1;
353                   break;
354               }
355 
356               case opc_tableswitch:
357               case opc_lookupswitch: {
358                 SwitchData sw = (SwitchData)inst.value;
359                 balance(sw.defaultLabel, depth);
360                 for (Enumeration<Label> e = sw.tab.elements() ; e.hasMoreElements();) {
361                     balance(e.nextElement(), depth);
362                 }
363                 return;
364               }
365 
366               case opc_try: {
367                 TryData td = (TryData)inst.value;
368                 for (Enumeration<CatchData> e = td.catches.elements() ; e.hasMoreElements();) {
369                     CatchData cd = e.nextElement();
370                     balance(cd.getLabel(), depth + 1);
371                 }
372                 break;
373               }
374             }
375         }
376     }
377 
378     /**
379      * Generate code
380      */
381     public void write(Environment env, DataOutputStream out,
382                       MemberDefinition field, ConstantPool tab)
383                  throws IOException {
384         //listing(System.out);
385 
386         if ((field != null) && field.getArguments() != null) {
387               int sum = 0;
388               @SuppressWarnings("unchecked")
389               Vector<MemberDefinition> v = (Vector<MemberDefinition>)field.getArguments();
390               for (Enumeration<MemberDefinition> e = v.elements(); e.hasMoreElements(); ) {
391                   MemberDefinition f = e.nextElement();
392                   sum += f.getType().stackSize();
393               }
394               maxvar = sum;
395         }
396 
397         // Make sure the stack balances.  Also calculate maxvar and maxstack
398         try {
399             balance(first, 0);
400         } catch (CompilerError e) {
401             System.out.println("ERROR: " + e);
402             listing(System.out);
403             throw e;
404         }
405 
406         // Assign PCs
407         int pc = 0, nexceptions = 0;
408         for (Instruction inst = first ; inst != null ; inst = inst.next) {
409             inst.pc = pc;
410             int sz = inst.size(tab);
411             if (pc<65536 && (pc+sz)>=65536) {
412                env.error(inst.where, "warn.method.too.long");
413             }
414             pc += sz;
415 
416             if (inst.opc == opc_try) {
417                 nexceptions += ((TryData)inst.value).catches.size();
418             }
419         }
420 
421         // Write header
422         out.writeShort(maxdepth);
423         out.writeShort(maxvar);
424         out.writeInt(maxpc = pc);
425 
426         // Generate code
427         for (Instruction inst = first.next ; inst != null ; inst = inst.next) {
428             inst.write(out, tab);
429         }
430 
431         // write exceptions
432         out.writeShort(nexceptions);
433         if (nexceptions > 0) {
434             //listing(System.out);
435             writeExceptions(env, out, tab, first, last);
436         }
437     }
438 
439     /**
440      * Write the exceptions table
441      */
442     void writeExceptions(Environment env, DataOutputStream out, ConstantPool tab, Instruction first, Instruction last) throws IOException {
443         for (Instruction inst = first ; inst != last.next ; inst = inst.next) {
444             if (inst.opc == opc_try) {
445                 TryData td = (TryData)inst.value;
446                 writeExceptions(env, out, tab, inst.next, td.getEndLabel());
447                 for (Enumeration<CatchData> e = td.catches.elements() ; e.hasMoreElements();) {
448                     CatchData cd = e.nextElement();
449                     //System.out.println("EXCEPTION: " + env.getSource() + ", pc=" + inst.pc + ", end=" + td.getEndLabel().pc + ", hdl=" + cd.getLabel().pc + ", tp=" + cd.getType());
450                     out.writeShort(inst.pc);
451                     out.writeShort(td.getEndLabel().pc);
452                     out.writeShort(cd.getLabel().pc);
453                     if (cd.getType() != null) {
454                         out.writeShort(tab.index(cd.getType()));
455                     } else {
456                         out.writeShort(0);
457                     }
458                 }
459                 inst = td.getEndLabel();
460             }
461         }
462     }
463 
464 //JCOV
465     /**
466      * Write the coverage table
467      */
468     public void writeCoverageTable(Environment env, ClassDefinition c, DataOutputStream out, ConstantPool tab, long whereField) throws IOException {
469         Vector<Cover> TableLot = new Vector<>();         /* Coverage table */
470         boolean begseg = false;
471         boolean begmeth = false;
472         @SuppressWarnings("deprecation")
473         long whereClass = ((SourceClass)c).getWhere();
474         Vector<Long> whereTry = new Vector<>();
475         int numberTry = 0;
476         int count = 0;
477 
478         for (Instruction inst = first ; inst != null ; inst = inst.next) {
479             long n = (inst.where >> WHEREOFFSETBITS);
480             if (n > 0 && inst.opc != opc_label) {
481                 if (!begmeth) {
482                   if ( whereClass == inst.where)
483                         TableLot.addElement(new Cover(CT_FIKT_METHOD, whereField, inst.pc));
484                   else
485                         TableLot.addElement(new Cover(CT_METHOD, whereField, inst.pc));
486                   count++;
487                   begmeth = true;
488                 }
489                 if (!begseg && !inst.flagNoCovered ) {
490                   boolean findTry = false;
491                   for (Enumeration<Long> e = whereTry.elements(); e.hasMoreElements();) {
492                        if (e.nextElement().longValue() == inst.where) {
493                               findTry = true;
494                               break;
495                        }
496                   }
497                   if (!findTry) {
498                       TableLot.addElement(new Cover(CT_BLOCK, inst.where, inst.pc));
499                       count++;
500                       begseg = true;
501                   }
502                 }
503             }
504             switch (inst.opc) {
505               case opc_label:
506                 begseg = false;
507                 break;
508               case opc_ifeq:
509               case opc_ifne:
510               case opc_ifnull:
511               case opc_ifnonnull:
512               case opc_ifgt:
513               case opc_ifge:
514               case opc_iflt:
515               case opc_ifle:
516               case opc_if_icmpeq:
517               case opc_if_icmpne:
518               case opc_if_icmpgt:
519               case opc_if_icmpge:
520               case opc_if_icmplt:
521               case opc_if_icmple:
522               case opc_if_acmpeq:
523               case opc_if_acmpne: {
524                 if ( inst.flagCondInverted ) {
525                    TableLot.addElement(new Cover(CT_BRANCH_TRUE, inst.where, inst.pc));
526                    TableLot.addElement(new Cover(CT_BRANCH_FALSE, inst.where, inst.pc));
527                 } else {
528                    TableLot.addElement(new Cover(CT_BRANCH_FALSE, inst.where, inst.pc));
529                    TableLot.addElement(new Cover(CT_BRANCH_TRUE, inst.where, inst.pc));
530                 }
531                 count += 2;
532                 begseg = false;
533                 break;
534               }
535 
536               case opc_goto: {
537                 begseg = false;
538                 break;
539               }
540 
541               case opc_ret:
542               case opc_return:
543               case opc_ireturn:
544               case opc_lreturn:
545               case opc_freturn:
546               case opc_dreturn:
547               case opc_areturn:
548               case opc_athrow: {
549                 break;
550               }
551 
552               case opc_try: {
553                 whereTry.addElement(Long.valueOf(inst.where));
554                 begseg = false;
555                 break;
556               }
557 
558               case opc_tableswitch: {
559                 SwitchData sw = (SwitchData)inst.value;
560                 for (int i = sw.minValue; i <= sw.maxValue; i++) {
561                      TableLot.addElement(new Cover(CT_CASE, sw.whereCase(new Integer(i)), inst.pc));
562                      count++;
563                 }
564                 if (!sw.getDefault()) {
565                      TableLot.addElement(new Cover(CT_SWITH_WO_DEF, inst.where, inst.pc));
566                      count++;
567                 } else {
568                      TableLot.addElement(new Cover(CT_CASE, sw.whereCase("default"), inst.pc));
569                      count++;
570                 }
571                 begseg = false;
572                 break;
573               }
574               case opc_lookupswitch: {
575                 SwitchData sw = (SwitchData)inst.value;
576                 for (Enumeration<Integer> e = sw.sortedKeys(); e.hasMoreElements() ; ) {
577                      Integer v = e.nextElement();
578                      TableLot.addElement(new Cover(CT_CASE, sw.whereCase(v), inst.pc));
579                      count++;
580                 }
581                 if (!sw.getDefault()) {
582                      TableLot.addElement(new Cover(CT_SWITH_WO_DEF, inst.where, inst.pc));
583                      count++;
584                 } else {
585                      TableLot.addElement(new Cover(CT_CASE, sw.whereCase("default"), inst.pc));
586                      count++;
587                 }
588                 begseg = false;
589                 break;
590               }
591             }
592         }
593         Cover Lot;
594         long ln, pos;
595 
596         out.writeShort(count);
597         for (int i = 0; i < count; i++) {
598            Lot = TableLot.elementAt(i);
599            ln = (Lot.Addr >> WHEREOFFSETBITS);
600            pos = (Lot.Addr << (64 - WHEREOFFSETBITS)) >> (64 - WHEREOFFSETBITS);
601            out.writeShort(Lot.NumCommand);
602            out.writeShort(Lot.Type);
603            out.writeInt((int)ln);
604            out.writeInt((int)pos);
605 
606            if ( !(Lot.Type == CT_CASE && Lot.Addr == 0) ) {
607                 JcovClassCountArray[Lot.Type]++;
608            }
609         }
610 
611     }
612 
613 /*
614  *  Increase count of methods for native methods
615  */
616 
617 public void addNativeToJcovTab(Environment env, ClassDefinition c) {
618         JcovClassCountArray[CT_METHOD]++;
619 }
620 
621 /*
622  *  Create class jcov element
623  */
624 
625 private String createClassJcovElement(Environment env, ClassDefinition c) {
626         String SourceClass = (Type.mangleInnerType((c.getClassDeclaration()).getName())).toString();
627         String ConvSourceClass;
628         String classJcovLine;
629 
630         SourceClassList.addElement(SourceClass);
631         ConvSourceClass = SourceClass.replace('.', '/');
632         classJcovLine = JcovClassLine + ConvSourceClass;
633 
634         classJcovLine = classJcovLine + " [";
635         String blank = "";
636 
637         for (int i = 0; i < arrayModifiers.length; i++ ) {
638             if ((c.getModifiers() & arrayModifiers[i]) != 0) {
639                 classJcovLine = classJcovLine + blank + opNames[arrayModifiersOpc[i]];
640                 blank = " ";
641             }
642         }
643         classJcovLine = classJcovLine + "]";
644 
645         return classJcovLine;
646 }
647 
648 /*
649  *  generate coverage data
650  */
651 
652 public void GenVecJCov(Environment env, ClassDefinition c, long Time) {
653         @SuppressWarnings("deprecation")
654         String SourceFile = ((SourceClass)c).getAbsoluteName();
655 
656         TmpCovTable.addElement(createClassJcovElement(env, c));
657         TmpCovTable.addElement(JcovSrcfileLine + SourceFile);
658         TmpCovTable.addElement(JcovTimestampLine + Time);
659         TmpCovTable.addElement(JcovDataLine + "A");             // data format
660         TmpCovTable.addElement(JcovHeadingLine);
661 
662         for (int i = CT_FIRST_KIND; i <= CT_LAST_KIND; i++) {
663             if (JcovClassCountArray[i] != 0) {
664                 TmpCovTable.addElement(new String(i + "\t" + JcovClassCountArray[i]));
665                 JcovClassCountArray[i] = 0;
666             }
667         }
668 }
669 
670 
671 /*
672  * generate file of coverage data
673  */
674 
675 @SuppressWarnings("deprecation") // for JCovd.readLine() calls
676 public void GenJCov(Environment env) {
677 
678      try {
679         File outFile = env.getcovFile();
680         if( outFile.exists()) {
681            DataInputStream JCovd = new DataInputStream(
682                                                        new BufferedInputStream(
683                                                                                new FileInputStream(outFile)));
684            String CurrLine = null;
685            boolean first = true;
686            String Class;
687 
688            CurrLine = JCovd.readLine();
689            if ((CurrLine != null) && CurrLine.startsWith(JcovMagicLine)) {
690                 // this is a good Jcov file
691 
692                    while((CurrLine = JCovd.readLine()) != null ) {
693                       if ( CurrLine.startsWith(JcovClassLine) ) {
694                              first = true;
695                              for(Enumeration<String> e = SourceClassList.elements(); e.hasMoreElements();) {
696                                  String clsName = CurrLine.substring(JcovClassLine.length());
697                                  int idx = clsName.indexOf(' ');
698 
699                                  if (idx != -1) {
700                                      clsName = clsName.substring(0, idx);
701                                  }
702                                  Class = e.nextElement();
703                                  if ( Class.compareTo(clsName) == 0) {
704                                      first = false;
705                                      break;
706                                  }
707                              }
708                       }
709                       if (first)        // re-write old class
710                           TmpCovTable.addElement(CurrLine);
711                    }
712            }
713            JCovd.close();
714         }
715         PrintStream CovFile = new PrintStream(new DataOutputStream(new FileOutputStream(outFile)));
716         CovFile.println(JcovMagicLine);
717         for(Enumeration<String> e = TmpCovTable.elements(); e.hasMoreElements();) {
718               CovFile.println(e.nextElement());
719         }
720         CovFile.close();
721     }
722     catch (FileNotFoundException e) {
723        System.out.println("ERROR: " + e);
724     }
725     catch (IOException e) {
726        System.out.println("ERROR: " + e);
727     }
728 }
729 // end JCOV
730 
731 
732     /**
733      * Write the linenumber table
734      */
735     public void writeLineNumberTable(Environment env, DataOutputStream out, ConstantPool tab) throws IOException {
736         long ln = -1;
737         int count = 0;
738 
739         for (Instruction inst = first ; inst != null ; inst = inst.next) {
740             long n = (inst.where >> WHEREOFFSETBITS);
741             if ((n > 0) && (ln != n)) {
742                 ln = n;
743                 count++;
744             }
745         }
746 
747         ln = -1;
748         out.writeShort(count);
749         for (Instruction inst = first ; inst != null ; inst = inst.next) {
750             long n = (inst.where >> WHEREOFFSETBITS);
751             if ((n > 0) && (ln != n)) {
752                 ln = n;
753                 out.writeShort(inst.pc);
754                 out.writeShort((int)ln);
755                 //System.out.println("pc = " + inst.pc + ", ln = " + ln);
756             }
757         }
758     }
759 
760     /**
761      * Figure out when registers contain a legal value. This is done
762      * using a simple data flow algorithm. This information is later used
763      * to generate the local variable table.
764      */
765     void flowFields(Environment env, Label lbl, MemberDefinition locals[]) {
766         if (lbl.locals != null) {
767             // Been here before. Erase any conflicts.
768             MemberDefinition f[] = lbl.locals;
769             for (int i = 0 ; i < maxvar ; i++) {
770                 if (f[i] != locals[i]) {
771                     f[i] = null;
772                 }
773             }
774             return;
775         }
776 
777         // Remember the set of active registers at this point
778         lbl.locals = new MemberDefinition[maxvar];
779         System.arraycopy(locals, 0, lbl.locals, 0, maxvar);
780 
781         MemberDefinition newlocals[] = new MemberDefinition[maxvar];
782         System.arraycopy(locals, 0, newlocals, 0, maxvar);
783         locals = newlocals;
784 
785         for (Instruction inst = lbl.next ; inst != null ; inst = inst.next)  {
786             switch (inst.opc) {
787               case opc_istore:   case opc_istore_0: case opc_istore_1:
788               case opc_istore_2: case opc_istore_3:
789               case opc_fstore:   case opc_fstore_0: case opc_fstore_1:
790               case opc_fstore_2: case opc_fstore_3:
791               case opc_astore:   case opc_astore_0: case opc_astore_1:
792               case opc_astore_2: case opc_astore_3:
793               case opc_lstore:   case opc_lstore_0: case opc_lstore_1:
794               case opc_lstore_2: case opc_lstore_3:
795               case opc_dstore:   case opc_dstore_0: case opc_dstore_1:
796               case opc_dstore_2: case opc_dstore_3:
797                 if (inst.value instanceof LocalVariable) {
798                     LocalVariable v = (LocalVariable)inst.value;
799                     locals[v.slot] = v.field;
800                 }
801                 break;
802 
803               case opc_label:
804                 flowFields(env, (Label)inst, locals);
805                 return;
806 
807               case opc_ifeq: case opc_ifne: case opc_ifgt:
808               case opc_ifge: case opc_iflt: case opc_ifle:
809               case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpgt:
810               case opc_if_icmpge: case opc_if_icmplt: case opc_if_icmple:
811               case opc_if_acmpeq: case opc_if_acmpne:
812               case opc_ifnull: case opc_ifnonnull:
813               case opc_jsr:
814                 flowFields(env, (Label)inst.value, locals);
815                 break;
816 
817               case opc_goto:
818                 flowFields(env, (Label)inst.value, locals);
819                 return;
820 
821               case opc_return:   case opc_ireturn:  case opc_lreturn:
822               case opc_freturn:  case opc_dreturn:  case opc_areturn:
823               case opc_athrow:   case opc_ret:
824                 return;
825 
826               case opc_tableswitch:
827               case opc_lookupswitch: {
828                 SwitchData sw = (SwitchData)inst.value;
829                 flowFields(env, sw.defaultLabel, locals);
830                 for (Enumeration<Label> e = sw.tab.elements() ; e.hasMoreElements();) {
831                     flowFields(env, e.nextElement(), locals);
832                 }
833                 return;
834               }
835 
836               case opc_try: {
837                 Vector<CatchData> catches = ((TryData)inst.value).catches;
838                 for (Enumeration<CatchData> e = catches.elements(); e.hasMoreElements();) {
839                     CatchData cd = e.nextElement();
840                     flowFields(env, cd.getLabel(), locals);
841                 }
842                 break;
843               }
844             }
845         }
846     }
847 
848     /**
849      * Write the local variable table. The necessary constants have already been
850      * added to the constant table by the collect() method. The flowFields method
851      * is used to determine which variables are alive at each pc.
852      */
853     public void writeLocalVariableTable(Environment env, MemberDefinition field, DataOutputStream out, ConstantPool tab) throws IOException {
854         MemberDefinition locals[] = new MemberDefinition[maxvar];
855         int i = 0;
856 
857         // Initialize arguments
858         if ((field != null) && (field.getArguments() != null)) {
859             int reg = 0;
860             @SuppressWarnings("unchecked")
861             Vector<MemberDefinition> v = (Vector<MemberDefinition>)field.getArguments();
862             for (Enumeration<MemberDefinition> e = v.elements(); e.hasMoreElements(); ) {
863                 MemberDefinition f = e.nextElement();
864                 locals[reg] = f;
865                 reg += f.getType().stackSize();
866             }
867         }
868 
869         flowFields(env, first, locals);
870         LocalVariableTable lvtab = new LocalVariableTable();
871 
872         // Initialize arguments again
873         for (i = 0; i < maxvar; i++)
874             locals[i] = null;
875         if ((field != null) && (field.getArguments() != null)) {
876             int reg = 0;
877             @SuppressWarnings("unchecked")
878             Vector<MemberDefinition> v = (Vector<MemberDefinition>)field.getArguments();
879             for (Enumeration<MemberDefinition> e = v.elements(); e.hasMoreElements(); ) {
880                 MemberDefinition f = e.nextElement();
881                 locals[reg] = f;
882                 lvtab.define(f, reg, 0, maxpc);
883                 reg += f.getType().stackSize();
884             }
885         }
886 
887         int pcs[] = new int[maxvar];
888 
889         for (Instruction inst = first ; inst != null ; inst = inst.next)  {
890             switch (inst.opc) {
891               case opc_istore:   case opc_istore_0: case opc_istore_1:
892               case opc_istore_2: case opc_istore_3: case opc_fstore:
893               case opc_fstore_0: case opc_fstore_1: case opc_fstore_2:
894               case opc_fstore_3:
895               case opc_astore:   case opc_astore_0: case opc_astore_1:
896               case opc_astore_2: case opc_astore_3:
897               case opc_lstore:   case opc_lstore_0: case opc_lstore_1:
898               case opc_lstore_2: case opc_lstore_3:
899               case opc_dstore:   case opc_dstore_0: case opc_dstore_1:
900               case opc_dstore_2: case opc_dstore_3:
901                 if (inst.value instanceof LocalVariable) {
902                     LocalVariable v = (LocalVariable)inst.value;
903                     int pc = (inst.next != null) ? inst.next.pc : inst.pc;
904                     if (locals[v.slot] != null) {
905                         lvtab.define(locals[v.slot], v.slot, pcs[v.slot], pc);
906                     }
907                     pcs[v.slot] = pc;
908                     locals[v.slot] = v.field;
909                 }
910                 break;
911 
912               case opc_label: {
913                 // flush  previous labels
914                 for (i = 0 ; i < maxvar ; i++) {
915                     if (locals[i] != null) {
916                         lvtab.define(locals[i], i, pcs[i], inst.pc);
917                     }
918                 }
919                 // init new labels
920                 int pc = inst.pc;
921                 MemberDefinition[] labelLocals = ((Label)inst).locals;
922                 if (labelLocals == null) { // unreachable code??
923                     for (i = 0; i < maxvar; i++)
924                         locals[i] = null;
925                 } else {
926                     System.arraycopy(labelLocals, 0, locals, 0, maxvar);
927                 }
928                 for (i = 0 ; i < maxvar ; i++) {
929                     pcs[i] = pc;
930                 }
931                 break;
932               }
933             }
934         }
935 
936         // flush  remaining labels
937         for (i = 0 ; i < maxvar ; i++) {
938             if (locals[i] != null) {
939                 lvtab.define(locals[i], i, pcs[i], maxpc);
940             }
941         }
942 
943         // write the local variable table
944         lvtab.write(env, out, tab);
945     }
946 
947     /**
948      * Return true if empty
949      */
950     public boolean empty() {
951         return first == last;
952     }
953 
954     /**
955      * Print the byte codes
956      */
957     public void listing(PrintStream out) {
958         out.println("-- listing --");
959         for (Instruction inst = first ; inst != null ; inst = inst.next) {
960             out.println(inst.toString());
961         }
962     }
963 }