| /* |
| * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.tools.asm; |
| |
| import sun.tools.java.*; |
| import java.util.Enumeration; |
| import java.io.IOException; |
| import java.io.DataOutputStream; |
| import java.io.PrintStream; |
| import java.util.Vector; |
| // JCOV |
| import sun.tools.javac.*; |
| import java.io.File; |
| import java.io.BufferedInputStream; |
| import java.io.DataInputStream; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.lang.String; |
| // end JCOV |
| |
| /** |
| * This class is used to assemble the bytecode instructions for a method. |
| * |
| * WARNING: The contents of this source file are not part of any |
| * supported API. Code that depends on them does so at its own risk: |
| * they are subject to change or removal without notice. |
| * |
| * @author Arthur van Hoff |
| */ |
| public final |
| class Assembler implements Constants { |
| static final int NOTREACHED = 0; |
| static final int REACHED = 1; |
| static final int NEEDED = 2; |
| |
| Label first = new Label(); |
| Instruction last = first; |
| int maxdepth; |
| int maxvar; |
| int maxpc; |
| |
| /** |
| * Add an instruction |
| */ |
| public void add(Instruction inst) { |
| if (inst != null) { |
| last.next = inst; |
| last = inst; |
| } |
| } |
| public void add(long where, int opc) { |
| add(new Instruction(where, opc, null)); |
| } |
| public void add(long where, int opc, Object obj) { |
| add(new Instruction(where, opc, obj)); |
| } |
| // JCOV |
| public void add(long where, int opc, Object obj, boolean flagCondInverted) { |
| add(new Instruction(where, opc, obj, flagCondInverted)); |
| } |
| |
| public void add(boolean flagNoCovered, long where, int opc, Object obj) { |
| add(new Instruction(flagNoCovered, where, opc, obj)); |
| } |
| |
| public void add(long where, int opc, boolean flagNoCovered) { |
| add(new Instruction(where, opc, flagNoCovered)); |
| } |
| |
| static Vector<String> SourceClassList = new Vector<>(); |
| |
| static Vector<String> TmpCovTable = new Vector<>(); |
| |
| static int[] JcovClassCountArray = new int[CT_LAST_KIND + 1]; |
| |
| static String JcovMagicLine = "JCOV-DATA-FILE-VERSION: 2.0"; |
| static String JcovClassLine = "CLASS: "; |
| static String JcovSrcfileLine = "SRCFILE: "; |
| static String JcovTimestampLine = "TIMESTAMP: "; |
| static String JcovDataLine = "DATA: "; |
| static String JcovHeadingLine = "#kind\tcount"; |
| |
| static int[] arrayModifiers = |
| {M_PUBLIC, M_PRIVATE, M_PROTECTED, M_ABSTRACT, M_FINAL, M_INTERFACE}; |
| static int[] arrayModifiersOpc = |
| {PUBLIC, PRIVATE, PROTECTED, ABSTRACT, FINAL, INTERFACE}; |
| //end JCOV |
| |
| /** |
| * Optimize instructions and mark those that can be reached |
| */ |
| void optimize(Environment env, Label lbl) { |
| lbl.pc = REACHED; |
| |
| for (Instruction inst = lbl.next ; inst != null ; inst = inst.next) { |
| switch (inst.pc) { |
| case NOTREACHED: |
| inst.optimize(env); |
| inst.pc = REACHED; |
| break; |
| case REACHED: |
| return; |
| case NEEDED: |
| break; |
| } |
| |
| switch (inst.opc) { |
| case opc_label: |
| case opc_dead: |
| if (inst.pc == REACHED) { |
| inst.pc = NOTREACHED; |
| } |
| break; |
| |
| case opc_ifeq: |
| case opc_ifne: |
| case opc_ifgt: |
| case opc_ifge: |
| case opc_iflt: |
| case opc_ifle: |
| case opc_if_icmpeq: |
| case opc_if_icmpne: |
| case opc_if_icmpgt: |
| case opc_if_icmpge: |
| case opc_if_icmplt: |
| case opc_if_icmple: |
| case opc_if_acmpeq: |
| case opc_if_acmpne: |
| case opc_ifnull: |
| case opc_ifnonnull: |
| optimize(env, (Label)inst.value); |
| break; |
| |
| case opc_goto: |
| optimize(env, (Label)inst.value); |
| return; |
| |
| case opc_jsr: |
| optimize(env, (Label)inst.value); |
| break; |
| |
| case opc_ret: |
| case opc_return: |
| case opc_ireturn: |
| case opc_lreturn: |
| case opc_freturn: |
| case opc_dreturn: |
| case opc_areturn: |
| case opc_athrow: |
| return; |
| |
| case opc_tableswitch: |
| case opc_lookupswitch: { |
| SwitchData sw = (SwitchData)inst.value; |
| optimize(env, sw.defaultLabel); |
| for (Enumeration<Label> e = sw.tab.elements() ; e.hasMoreElements();) { |
| optimize(env, e.nextElement()); |
| } |
| return; |
| } |
| |
| case opc_try: { |
| TryData td = (TryData)inst.value; |
| td.getEndLabel().pc = NEEDED; |
| for (Enumeration<CatchData> e = td.catches.elements() ; e.hasMoreElements();) { |
| CatchData cd = e.nextElement(); |
| optimize(env, cd.getLabel()); |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Eliminate instructions that are not reached |
| */ |
| boolean eliminate() { |
| boolean change = false; |
| Instruction prev = first; |
| |
| for (Instruction inst = first.next ; inst != null ; inst = inst.next) { |
| if (inst.pc != NOTREACHED) { |
| prev.next = inst; |
| prev = inst; |
| inst.pc = NOTREACHED; |
| } else { |
| change = true; |
| } |
| } |
| first.pc = NOTREACHED; |
| prev.next = null; |
| return change; |
| } |
| |
| /** |
| * Optimize the byte codes |
| */ |
| public void optimize(Environment env) { |
| //listing(System.out); |
| do { |
| // Figure out which instructions are reached |
| optimize(env, first); |
| |
| // Eliminate instructions that are not reached |
| } while (eliminate() && env.opt()); |
| } |
| |
| /** |
| * Collect all constants into the constant table |
| */ |
| public void collect(Environment env, MemberDefinition field, ConstantPool tab) { |
| // Collect constants for arguments only |
| // if a local variable table is generated |
| if ((field != null) && env.debug_vars()) { |
| @SuppressWarnings("unchecked") |
| Vector<MemberDefinition> v = field.getArguments(); |
| if (v != null) { |
| for (Enumeration<MemberDefinition> e = v.elements() ; e.hasMoreElements() ;) { |
| MemberDefinition f = e.nextElement(); |
| tab.put(f.getName().toString()); |
| tab.put(f.getType().getTypeSignature()); |
| } |
| } |
| } |
| |
| // Collect constants from the instructions |
| for (Instruction inst = first ; inst != null ; inst = inst.next) { |
| inst.collect(tab); |
| } |
| } |
| |
| /** |
| * Determine stack size, count local variables |
| */ |
| void balance(Label lbl, int depth) { |
| for (Instruction inst = lbl ; inst != null ; inst = inst.next) { |
| //Environment.debugOutput(inst.toString() + ": " + depth + " => " + |
| // (depth + inst.balance())); |
| depth += inst.balance(); |
| if (depth < 0) { |
| throw new CompilerError("stack under flow: " + inst.toString() + " = " + depth); |
| } |
| if (depth > maxdepth) { |
| maxdepth = depth; |
| } |
| switch (inst.opc) { |
| case opc_label: |
| lbl = (Label)inst; |
| if (inst.pc == REACHED) { |
| if (lbl.depth != depth) { |
| throw new CompilerError("stack depth error " + |
| depth + "/" + lbl.depth + |
| ": " + inst.toString()); |
| } |
| return; |
| } |
| lbl.pc = REACHED; |
| lbl.depth = depth; |
| break; |
| |
| case opc_ifeq: |
| case opc_ifne: |
| case opc_ifgt: |
| case opc_ifge: |
| case opc_iflt: |
| case opc_ifle: |
| case opc_if_icmpeq: |
| case opc_if_icmpne: |
| case opc_if_icmpgt: |
| case opc_if_icmpge: |
| case opc_if_icmplt: |
| case opc_if_icmple: |
| case opc_if_acmpeq: |
| case opc_if_acmpne: |
| case opc_ifnull: |
| case opc_ifnonnull: |
| balance((Label)inst.value, depth); |
| break; |
| |
| case opc_goto: |
| balance((Label)inst.value, depth); |
| return; |
| |
| case opc_jsr: |
| balance((Label)inst.value, depth + 1); |
| break; |
| |
| case opc_ret: |
| case opc_return: |
| case opc_ireturn: |
| case opc_lreturn: |
| case opc_freturn: |
| case opc_dreturn: |
| case opc_areturn: |
| case opc_athrow: |
| return; |
| |
| case opc_iload: |
| case opc_fload: |
| case opc_aload: |
| case opc_istore: |
| case opc_fstore: |
| case opc_astore: { |
| int v = ((inst.value instanceof Number) |
| ? ((Number)inst.value).intValue() |
| : ((LocalVariable)inst.value).slot) + 1; |
| if (v > maxvar) |
| maxvar = v; |
| break; |
| } |
| |
| case opc_lload: |
| case opc_dload: |
| case opc_lstore: |
| case opc_dstore: { |
| int v = ((inst.value instanceof Number) |
| ? ((Number)inst.value).intValue() |
| : ((LocalVariable)inst.value).slot) + 2; |
| if (v > maxvar) |
| maxvar = v; |
| break; |
| } |
| |
| case opc_iinc: { |
| int v = ((int[])inst.value)[0] + 1; |
| if (v > maxvar) |
| maxvar = v + 1; |
| break; |
| } |
| |
| case opc_tableswitch: |
| case opc_lookupswitch: { |
| SwitchData sw = (SwitchData)inst.value; |
| balance(sw.defaultLabel, depth); |
| for (Enumeration<Label> e = sw.tab.elements() ; e.hasMoreElements();) { |
| balance(e.nextElement(), depth); |
| } |
| return; |
| } |
| |
| case opc_try: { |
| TryData td = (TryData)inst.value; |
| for (Enumeration<CatchData> e = td.catches.elements() ; e.hasMoreElements();) { |
| CatchData cd = e.nextElement(); |
| balance(cd.getLabel(), depth + 1); |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Generate code |
| */ |
| public void write(Environment env, DataOutputStream out, |
| MemberDefinition field, ConstantPool tab) |
| throws IOException { |
| //listing(System.out); |
| |
| if ((field != null) && field.getArguments() != null) { |
| int sum = 0; |
| @SuppressWarnings("unchecked") |
| Vector<MemberDefinition> v = field.getArguments(); |
| for (Enumeration<MemberDefinition> e = v.elements(); e.hasMoreElements(); ) { |
| MemberDefinition f = e.nextElement(); |
| sum += f.getType().stackSize(); |
| } |
| maxvar = sum; |
| } |
| |
| // Make sure the stack balances. Also calculate maxvar and maxstack |
| try { |
| balance(first, 0); |
| } catch (CompilerError e) { |
| System.out.println("ERROR: " + e); |
| listing(System.out); |
| throw e; |
| } |
| |
| // Assign PCs |
| int pc = 0, nexceptions = 0; |
| for (Instruction inst = first ; inst != null ; inst = inst.next) { |
| inst.pc = pc; |
| int sz = inst.size(tab); |
| if (pc<65536 && (pc+sz)>=65536) { |
| env.error(inst.where, "warn.method.too.long"); |
| } |
| pc += sz; |
| |
| if (inst.opc == opc_try) { |
| nexceptions += ((TryData)inst.value).catches.size(); |
| } |
| } |
| |
| // Write header |
| out.writeShort(maxdepth); |
| out.writeShort(maxvar); |
| out.writeInt(maxpc = pc); |
| |
| // Generate code |
| for (Instruction inst = first.next ; inst != null ; inst = inst.next) { |
| inst.write(out, tab); |
| } |
| |
| // write exceptions |
| out.writeShort(nexceptions); |
| if (nexceptions > 0) { |
| //listing(System.out); |
| writeExceptions(env, out, tab, first, last); |
| } |
| } |
| |
| /** |
| * Write the exceptions table |
| */ |
| void writeExceptions(Environment env, DataOutputStream out, ConstantPool tab, Instruction first, Instruction last) throws IOException { |
| for (Instruction inst = first ; inst != last.next ; inst = inst.next) { |
| if (inst.opc == opc_try) { |
| TryData td = (TryData)inst.value; |
| writeExceptions(env, out, tab, inst.next, td.getEndLabel()); |
| for (Enumeration<CatchData> e = td.catches.elements() ; e.hasMoreElements();) { |
| CatchData cd = e.nextElement(); |
| //System.out.println("EXCEPTION: " + env.getSource() + ", pc=" + inst.pc + ", end=" + td.getEndLabel().pc + ", hdl=" + cd.getLabel().pc + ", tp=" + cd.getType()); |
| out.writeShort(inst.pc); |
| out.writeShort(td.getEndLabel().pc); |
| out.writeShort(cd.getLabel().pc); |
| if (cd.getType() != null) { |
| out.writeShort(tab.index(cd.getType())); |
| } else { |
| out.writeShort(0); |
| } |
| } |
| inst = td.getEndLabel(); |
| } |
| } |
| } |
| |
| //JCOV |
| /** |
| * Write the coverage table |
| */ |
| public void writeCoverageTable(Environment env, ClassDefinition c, DataOutputStream out, ConstantPool tab, long whereField) throws IOException { |
| Vector<Cover> TableLot = new Vector<>(); /* Coverage table */ |
| boolean begseg = false; |
| boolean begmeth = false; |
| @SuppressWarnings("deprecation") |
| long whereClass = ((SourceClass)c).getWhere(); |
| Vector<Long> whereTry = new Vector<>(); |
| int numberTry = 0; |
| int count = 0; |
| |
| for (Instruction inst = first ; inst != null ; inst = inst.next) { |
| long n = (inst.where >> WHEREOFFSETBITS); |
| if (n > 0 && inst.opc != opc_label) { |
| if (!begmeth) { |
| if ( whereClass == inst.where) |
| TableLot.addElement(new Cover(CT_FIKT_METHOD, whereField, inst.pc)); |
| else |
| TableLot.addElement(new Cover(CT_METHOD, whereField, inst.pc)); |
| count++; |
| begmeth = true; |
| } |
| if (!begseg && !inst.flagNoCovered ) { |
| boolean findTry = false; |
| for (Enumeration<Long> e = whereTry.elements(); e.hasMoreElements();) { |
| if (e.nextElement().longValue() == inst.where) { |
| findTry = true; |
| break; |
| } |
| } |
| if (!findTry) { |
| TableLot.addElement(new Cover(CT_BLOCK, inst.where, inst.pc)); |
| count++; |
| begseg = true; |
| } |
| } |
| } |
| switch (inst.opc) { |
| case opc_label: |
| begseg = false; |
| break; |
| case opc_ifeq: |
| case opc_ifne: |
| case opc_ifnull: |
| case opc_ifnonnull: |
| case opc_ifgt: |
| case opc_ifge: |
| case opc_iflt: |
| case opc_ifle: |
| case opc_if_icmpeq: |
| case opc_if_icmpne: |
| case opc_if_icmpgt: |
| case opc_if_icmpge: |
| case opc_if_icmplt: |
| case opc_if_icmple: |
| case opc_if_acmpeq: |
| case opc_if_acmpne: { |
| if ( inst.flagCondInverted ) { |
| TableLot.addElement(new Cover(CT_BRANCH_TRUE, inst.where, inst.pc)); |
| TableLot.addElement(new Cover(CT_BRANCH_FALSE, inst.where, inst.pc)); |
| } else { |
| TableLot.addElement(new Cover(CT_BRANCH_FALSE, inst.where, inst.pc)); |
| TableLot.addElement(new Cover(CT_BRANCH_TRUE, inst.where, inst.pc)); |
| } |
| count += 2; |
| begseg = false; |
| break; |
| } |
| |
| case opc_goto: { |
| begseg = false; |
| break; |
| } |
| |
| case opc_ret: |
| case opc_return: |
| case opc_ireturn: |
| case opc_lreturn: |
| case opc_freturn: |
| case opc_dreturn: |
| case opc_areturn: |
| case opc_athrow: { |
| break; |
| } |
| |
| case opc_try: { |
| whereTry.addElement(Long.valueOf(inst.where)); |
| begseg = false; |
| break; |
| } |
| |
| case opc_tableswitch: { |
| SwitchData sw = (SwitchData)inst.value; |
| for (int i = sw.minValue; i <= sw.maxValue; i++) { |
| TableLot.addElement(new Cover(CT_CASE, sw.whereCase(i), inst.pc)); |
| count++; |
| } |
| if (!sw.getDefault()) { |
| TableLot.addElement(new Cover(CT_SWITH_WO_DEF, inst.where, inst.pc)); |
| count++; |
| } else { |
| TableLot.addElement(new Cover(CT_CASE, sw.whereCase("default"), inst.pc)); |
| count++; |
| } |
| begseg = false; |
| break; |
| } |
| case opc_lookupswitch: { |
| SwitchData sw = (SwitchData)inst.value; |
| for (Enumeration<Integer> e = sw.sortedKeys(); e.hasMoreElements() ; ) { |
| Integer v = e.nextElement(); |
| TableLot.addElement(new Cover(CT_CASE, sw.whereCase(v), inst.pc)); |
| count++; |
| } |
| if (!sw.getDefault()) { |
| TableLot.addElement(new Cover(CT_SWITH_WO_DEF, inst.where, inst.pc)); |
| count++; |
| } else { |
| TableLot.addElement(new Cover(CT_CASE, sw.whereCase("default"), inst.pc)); |
| count++; |
| } |
| begseg = false; |
| break; |
| } |
| } |
| } |
| Cover Lot; |
| long ln, pos; |
| |
| out.writeShort(count); |
| for (int i = 0; i < count; i++) { |
| Lot = TableLot.elementAt(i); |
| ln = (Lot.Addr >> WHEREOFFSETBITS); |
| pos = (Lot.Addr << (64 - WHEREOFFSETBITS)) >> (64 - WHEREOFFSETBITS); |
| out.writeShort(Lot.NumCommand); |
| out.writeShort(Lot.Type); |
| out.writeInt((int)ln); |
| out.writeInt((int)pos); |
| |
| if ( !(Lot.Type == CT_CASE && Lot.Addr == 0) ) { |
| JcovClassCountArray[Lot.Type]++; |
| } |
| } |
| |
| } |
| |
| /* |
| * Increase count of methods for native methods |
| */ |
| |
| public void addNativeToJcovTab(Environment env, ClassDefinition c) { |
| JcovClassCountArray[CT_METHOD]++; |
| } |
| |
| /* |
| * Create class jcov element |
| */ |
| |
| private String createClassJcovElement(Environment env, ClassDefinition c) { |
| String SourceClass = (Type.mangleInnerType((c.getClassDeclaration()).getName())).toString(); |
| String ConvSourceClass; |
| String classJcovLine; |
| |
| SourceClassList.addElement(SourceClass); |
| ConvSourceClass = SourceClass.replace('.', '/'); |
| classJcovLine = JcovClassLine + ConvSourceClass; |
| |
| classJcovLine = classJcovLine + " ["; |
| String blank = ""; |
| |
| for (int i = 0; i < arrayModifiers.length; i++ ) { |
| if ((c.getModifiers() & arrayModifiers[i]) != 0) { |
| classJcovLine = classJcovLine + blank + opNames[arrayModifiersOpc[i]]; |
| blank = " "; |
| } |
| } |
| classJcovLine = classJcovLine + "]"; |
| |
| return classJcovLine; |
| } |
| |
| /* |
| * generate coverage data |
| */ |
| |
| public void GenVecJCov(Environment env, ClassDefinition c, long Time) { |
| @SuppressWarnings("deprecation") |
| String SourceFile = ((SourceClass)c).getAbsoluteName(); |
| |
| TmpCovTable.addElement(createClassJcovElement(env, c)); |
| TmpCovTable.addElement(JcovSrcfileLine + SourceFile); |
| TmpCovTable.addElement(JcovTimestampLine + Time); |
| TmpCovTable.addElement(JcovDataLine + "A"); // data format |
| TmpCovTable.addElement(JcovHeadingLine); |
| |
| for (int i = CT_FIRST_KIND; i <= CT_LAST_KIND; i++) { |
| if (JcovClassCountArray[i] != 0) { |
| TmpCovTable.addElement(new String(i + "\t" + JcovClassCountArray[i])); |
| JcovClassCountArray[i] = 0; |
| } |
| } |
| } |
| |
| |
| /* |
| * generate file of coverage data |
| */ |
| |
| @SuppressWarnings("deprecation") // for JCovd.readLine() calls |
| public void GenJCov(Environment env) { |
| |
| try { |
| File outFile = env.getcovFile(); |
| if( outFile.exists()) { |
| DataInputStream JCovd = new DataInputStream( |
| new BufferedInputStream( |
| new FileInputStream(outFile))); |
| String CurrLine = null; |
| boolean first = true; |
| String Class; |
| |
| CurrLine = JCovd.readLine(); |
| if ((CurrLine != null) && CurrLine.startsWith(JcovMagicLine)) { |
| // this is a good Jcov file |
| |
| while((CurrLine = JCovd.readLine()) != null ) { |
| if ( CurrLine.startsWith(JcovClassLine) ) { |
| first = true; |
| for(Enumeration<String> e = SourceClassList.elements(); e.hasMoreElements();) { |
| String clsName = CurrLine.substring(JcovClassLine.length()); |
| int idx = clsName.indexOf(' '); |
| |
| if (idx != -1) { |
| clsName = clsName.substring(0, idx); |
| } |
| Class = e.nextElement(); |
| if ( Class.compareTo(clsName) == 0) { |
| first = false; |
| break; |
| } |
| } |
| } |
| if (first) // re-write old class |
| TmpCovTable.addElement(CurrLine); |
| } |
| } |
| JCovd.close(); |
| } |
| PrintStream CovFile = new PrintStream(new DataOutputStream(new FileOutputStream(outFile))); |
| CovFile.println(JcovMagicLine); |
| for(Enumeration<String> e = TmpCovTable.elements(); e.hasMoreElements();) { |
| CovFile.println(e.nextElement()); |
| } |
| CovFile.close(); |
| } |
| catch (FileNotFoundException e) { |
| System.out.println("ERROR: " + e); |
| } |
| catch (IOException e) { |
| System.out.println("ERROR: " + e); |
| } |
| } |
| // end JCOV |
| |
| |
| /** |
| * Write the linenumber table |
| */ |
| public void writeLineNumberTable(Environment env, DataOutputStream out, ConstantPool tab) throws IOException { |
| long ln = -1; |
| int count = 0; |
| |
| for (Instruction inst = first ; inst != null ; inst = inst.next) { |
| long n = (inst.where >> WHEREOFFSETBITS); |
| if ((n > 0) && (ln != n)) { |
| ln = n; |
| count++; |
| } |
| } |
| |
| ln = -1; |
| out.writeShort(count); |
| for (Instruction inst = first ; inst != null ; inst = inst.next) { |
| long n = (inst.where >> WHEREOFFSETBITS); |
| if ((n > 0) && (ln != n)) { |
| ln = n; |
| out.writeShort(inst.pc); |
| out.writeShort((int)ln); |
| //System.out.println("pc = " + inst.pc + ", ln = " + ln); |
| } |
| } |
| } |
| |
| /** |
| * Figure out when registers contain a legal value. This is done |
| * using a simple data flow algorithm. This information is later used |
| * to generate the local variable table. |
| */ |
| void flowFields(Environment env, Label lbl, MemberDefinition locals[]) { |
| if (lbl.locals != null) { |
| // Been here before. Erase any conflicts. |
| MemberDefinition f[] = lbl.locals; |
| for (int i = 0 ; i < maxvar ; i++) { |
| if (f[i] != locals[i]) { |
| f[i] = null; |
| } |
| } |
| return; |
| } |
| |
| // Remember the set of active registers at this point |
| lbl.locals = new MemberDefinition[maxvar]; |
| System.arraycopy(locals, 0, lbl.locals, 0, maxvar); |
| |
| MemberDefinition newlocals[] = new MemberDefinition[maxvar]; |
| System.arraycopy(locals, 0, newlocals, 0, maxvar); |
| locals = newlocals; |
| |
| for (Instruction inst = lbl.next ; inst != null ; inst = inst.next) { |
| switch (inst.opc) { |
| case opc_istore: case opc_istore_0: case opc_istore_1: |
| case opc_istore_2: case opc_istore_3: |
| case opc_fstore: case opc_fstore_0: case opc_fstore_1: |
| case opc_fstore_2: case opc_fstore_3: |
| case opc_astore: case opc_astore_0: case opc_astore_1: |
| case opc_astore_2: case opc_astore_3: |
| case opc_lstore: case opc_lstore_0: case opc_lstore_1: |
| case opc_lstore_2: case opc_lstore_3: |
| case opc_dstore: case opc_dstore_0: case opc_dstore_1: |
| case opc_dstore_2: case opc_dstore_3: |
| if (inst.value instanceof LocalVariable) { |
| LocalVariable v = (LocalVariable)inst.value; |
| locals[v.slot] = v.field; |
| } |
| break; |
| |
| case opc_label: |
| flowFields(env, (Label)inst, locals); |
| return; |
| |
| case opc_ifeq: case opc_ifne: case opc_ifgt: |
| case opc_ifge: case opc_iflt: case opc_ifle: |
| case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpgt: |
| case opc_if_icmpge: case opc_if_icmplt: case opc_if_icmple: |
| case opc_if_acmpeq: case opc_if_acmpne: |
| case opc_ifnull: case opc_ifnonnull: |
| case opc_jsr: |
| flowFields(env, (Label)inst.value, locals); |
| break; |
| |
| case opc_goto: |
| flowFields(env, (Label)inst.value, locals); |
| return; |
| |
| case opc_return: case opc_ireturn: case opc_lreturn: |
| case opc_freturn: case opc_dreturn: case opc_areturn: |
| case opc_athrow: case opc_ret: |
| return; |
| |
| case opc_tableswitch: |
| case opc_lookupswitch: { |
| SwitchData sw = (SwitchData)inst.value; |
| flowFields(env, sw.defaultLabel, locals); |
| for (Enumeration<Label> e = sw.tab.elements() ; e.hasMoreElements();) { |
| flowFields(env, e.nextElement(), locals); |
| } |
| return; |
| } |
| |
| case opc_try: { |
| Vector<CatchData> catches = ((TryData)inst.value).catches; |
| for (Enumeration<CatchData> e = catches.elements(); e.hasMoreElements();) { |
| CatchData cd = e.nextElement(); |
| flowFields(env, cd.getLabel(), locals); |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Write the local variable table. The necessary constants have already been |
| * added to the constant table by the collect() method. The flowFields method |
| * is used to determine which variables are alive at each pc. |
| */ |
| public void writeLocalVariableTable(Environment env, MemberDefinition field, DataOutputStream out, ConstantPool tab) throws IOException { |
| MemberDefinition locals[] = new MemberDefinition[maxvar]; |
| int i = 0; |
| |
| // Initialize arguments |
| if ((field != null) && (field.getArguments() != null)) { |
| int reg = 0; |
| @SuppressWarnings("unchecked") |
| Vector<MemberDefinition> v = field.getArguments(); |
| for (Enumeration<MemberDefinition> e = v.elements(); e.hasMoreElements(); ) { |
| MemberDefinition f = e.nextElement(); |
| locals[reg] = f; |
| reg += f.getType().stackSize(); |
| } |
| } |
| |
| flowFields(env, first, locals); |
| LocalVariableTable lvtab = new LocalVariableTable(); |
| |
| // Initialize arguments again |
| for (i = 0; i < maxvar; i++) |
| locals[i] = null; |
| if ((field != null) && (field.getArguments() != null)) { |
| int reg = 0; |
| @SuppressWarnings("unchecked") |
| Vector<MemberDefinition> v = field.getArguments(); |
| for (Enumeration<MemberDefinition> e = v.elements(); e.hasMoreElements(); ) { |
| MemberDefinition f = e.nextElement(); |
| locals[reg] = f; |
| lvtab.define(f, reg, 0, maxpc); |
| reg += f.getType().stackSize(); |
| } |
| } |
| |
| int pcs[] = new int[maxvar]; |
| |
| for (Instruction inst = first ; inst != null ; inst = inst.next) { |
| switch (inst.opc) { |
| case opc_istore: case opc_istore_0: case opc_istore_1: |
| case opc_istore_2: case opc_istore_3: case opc_fstore: |
| case opc_fstore_0: case opc_fstore_1: case opc_fstore_2: |
| case opc_fstore_3: |
| case opc_astore: case opc_astore_0: case opc_astore_1: |
| case opc_astore_2: case opc_astore_3: |
| case opc_lstore: case opc_lstore_0: case opc_lstore_1: |
| case opc_lstore_2: case opc_lstore_3: |
| case opc_dstore: case opc_dstore_0: case opc_dstore_1: |
| case opc_dstore_2: case opc_dstore_3: |
| if (inst.value instanceof LocalVariable) { |
| LocalVariable v = (LocalVariable)inst.value; |
| int pc = (inst.next != null) ? inst.next.pc : inst.pc; |
| if (locals[v.slot] != null) { |
| lvtab.define(locals[v.slot], v.slot, pcs[v.slot], pc); |
| } |
| pcs[v.slot] = pc; |
| locals[v.slot] = v.field; |
| } |
| break; |
| |
| case opc_label: { |
| // flush previous labels |
| for (i = 0 ; i < maxvar ; i++) { |
| if (locals[i] != null) { |
| lvtab.define(locals[i], i, pcs[i], inst.pc); |
| } |
| } |
| // init new labels |
| int pc = inst.pc; |
| MemberDefinition[] labelLocals = ((Label)inst).locals; |
| if (labelLocals == null) { // unreachable code?? |
| for (i = 0; i < maxvar; i++) |
| locals[i] = null; |
| } else { |
| System.arraycopy(labelLocals, 0, locals, 0, maxvar); |
| } |
| for (i = 0 ; i < maxvar ; i++) { |
| pcs[i] = pc; |
| } |
| break; |
| } |
| } |
| } |
| |
| // flush remaining labels |
| for (i = 0 ; i < maxvar ; i++) { |
| if (locals[i] != null) { |
| lvtab.define(locals[i], i, pcs[i], maxpc); |
| } |
| } |
| |
| // write the local variable table |
| lvtab.write(env, out, tab); |
| } |
| |
| /** |
| * Return true if empty |
| */ |
| public boolean empty() { |
| return first == last; |
| } |
| |
| /** |
| * Print the byte codes |
| */ |
| public void listing(PrintStream out) { |
| out.println("-- listing --"); |
| for (Instruction inst = first ; inst != null ; inst = inst.next) { |
| out.println(inst.toString()); |
| } |
| } |
| } |