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
26package sun.tools.asm;
27
28import sun.tools.java.*;
29import java.util.Enumeration;
30import java.io.IOException;
31import java.io.DataOutputStream;
32import java.io.PrintStream;
33import java.util.Vector;
34// JCOV
35import sun.tools.javac.*;
36import java.io.File;
37import java.io.BufferedInputStream;
38import java.io.DataInputStream;
39import java.io.FileInputStream;
40import java.io.FileNotFoundException;
41import java.io.FileOutputStream;
42import 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 */
54public final
55class 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 = 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 = 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(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
617public void addNativeToJcovTab(Environment env, ClassDefinition c) {
618        JcovClassCountArray[CT_METHOD]++;
619}
620
621/*
622 *  Create class jcov element
623 */
624
625private 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
652public 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
676public 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 = 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 = 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}
964