00001 package de.fub.bytecode.generic;
00002
00003 import de.fub.bytecode.Constants;
00004 import de.fub.bytecode.classfile.*;
00005 import java.util.*;
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 public class MethodGen extends FieldGenOrMethodGen {
00024 private String class_name;
00025 private Type[] arg_types;
00026 private String[] arg_names;
00027 private int max_locals;
00028 private int max_stack;
00029 private InstructionList il;
00030 private boolean strip_attributes;
00031
00032 private Vector variable_vec = new Vector();
00033 private Vector line_number_vec = new Vector();
00034 private Vector exception_vec = new Vector();
00035 private Vector throws_vec = new Vector();
00036 private Vector code_attrs_vec = new Vector();
00037
00038 static final class BranchTarget {
00039 InstructionHandle target;
00040 int stackDepth;
00041
00042 BranchTarget(InstructionHandle target, int stackDepth) {
00043 this.target = target;
00044 this.stackDepth = stackDepth;
00045 }
00046 }
00047
00048 static final class BranchStack {
00049 Stack branchTargets = new Stack();
00050 Hashtable visitedTargets = new Hashtable();
00051
00052 public void push(InstructionHandle target, int stackDepth) {
00053 if(visited(target))
00054 return;
00055
00056 branchTargets.push(visit(target, stackDepth));
00057 }
00058
00059 public BranchTarget pop() {
00060 if(!branchTargets.empty()) {
00061 BranchTarget bt = (BranchTarget) branchTargets.pop();
00062 return bt;
00063 }
00064
00065 return null;
00066 }
00067
00068 private final BranchTarget visit(InstructionHandle target, int stackDepth) {
00069 BranchTarget bt = new BranchTarget(target, stackDepth);
00070 visitedTargets.put(target, bt);
00071
00072 return bt;
00073 }
00074
00075 private final boolean visited(InstructionHandle target) {
00076 return (visitedTargets.get(target) != null);
00077 }
00078 }
00079
00080 private Vector observers;
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104 public MethodGen(int access_flags, Type return_type, Type[] arg_types,
00105 String[] arg_names, String method_name, String class_name,
00106 InstructionList il, ConstantPoolGen cp) {
00107 setAccessFlags(access_flags);
00108 setType(return_type);
00109 setArgTypes(arg_types);
00110 setArgNames(arg_names);
00111 setName(method_name);
00112 setClassName(class_name);
00113 setInstructionList(il);
00114 setConstantPool(cp);
00115
00116 if((access_flags & (Constants.ACC_ABSTRACT | Constants.ACC_NATIVE)) == 0) {
00117 InstructionHandle start = il.getStart();
00118 InstructionHandle end = il.getEnd();
00119
00120
00121
00122 if(!isStatic() && (class_name != null))
00123 addLocalVariable("this", new ObjectType(class_name), start, end);
00124
00125 if(arg_types != null) {
00126 int size = arg_types.length;
00127
00128 if(arg_names != null) {
00129 if(size != arg_names.length)
00130 throw new ClassGenException("Mismatch in argument array lengths: " +
00131 size + " vs. " + arg_names.length);
00132 } else {
00133 arg_names = new String[size];
00134
00135 for(int i=0; i < size; i++)
00136 arg_names[i] = "arg" + i;
00137 }
00138
00139 for(int i=0; i < size; i++)
00140 addLocalVariable(arg_names[i], arg_types[i], start, end);
00141 }
00142 }
00143 }
00144
00145
00146
00147
00148
00149
00150
00151 public MethodGen(Method m, String class_name, ConstantPoolGen cp) {
00152 this(m.getAccessFlags(), Type.getReturnType(m.getSignature()),
00153 Type.getArgumentTypes(m.getSignature()), null ,
00154 m.getName(), class_name,
00155 ((m.getAccessFlags() & (Constants.ACC_ABSTRACT | Constants.ACC_NATIVE)) == 0)?
00156 new InstructionList(m.getCode().getCode()) : null,
00157 cp);
00158
00159 Attribute[] attributes = m.getAttributes();
00160 for(int i=0; i < attributes.length; i++) {
00161 Attribute a = attributes[i];
00162
00163 if(a instanceof Code) {
00164 Code c = (Code)a;
00165 setMaxStack(c.getMaxStack());
00166 setMaxLocals(c.getMaxLocals());
00167
00168 CodeException[] ces = c.getExceptionTable();
00169
00170 if(ces != null) {
00171 for(int j=0; j < ces.length; j++) {
00172 CodeException ce = ces[j];
00173 int type = ce.getCatchType();
00174 ObjectType c_type = null;
00175
00176 if(type > 0)
00177 c_type = new ObjectType(cp.getConstantPool().
00178 getConstantString(type, Constants.CONSTANT_Class));
00179 int end_pc = ce.getEndPC();
00180 int length = m.getCode().getCode().length;
00181
00182 if(length == end_pc)
00183 end_pc = end_pc - il.getEnd().getInstruction().getLength();
00184
00185 addExceptionHandler(il.findHandle(ce.getStartPC()), il.findHandle(end_pc),
00186 il.findHandle(ce.getHandlerPC()), c_type);
00187 }
00188 }
00189
00190 Attribute[] c_attributes = c.getAttributes();
00191 for(int j=0; j < c_attributes.length; j++) {
00192 a = c_attributes[j];
00193
00194 if(a instanceof LineNumberTable) {
00195 LineNumber[] ln = ((LineNumberTable)a).getLineNumberTable();
00196 for(int k=0; k < ln.length; k++) {
00197 LineNumber l = ln[k];
00198 addLineNumber(il.findHandle(l.getStartPC()), l.getLineNumber());
00199 }
00200 }
00201 else if(a instanceof LocalVariableTable) {
00202 LocalVariable[] lv = ((LocalVariableTable)a).getLocalVariableTable();
00203 for(int k=0; k < lv.length; k++) {
00204 LocalVariable l = lv[k];
00205 InstructionHandle start = il.findHandle(l.getStartPC());
00206 InstructionHandle end = il.findHandle(l.getStartPC() + l.getLength());
00207
00208
00209 if(start == null)
00210 start = il.getStart();
00211 if(end == null)
00212 end = il.getEnd();
00213
00214 addLocalVariable(l.getName(), Type.getType(l.getSignature()),
00215 l.getIndex(), start, end);
00216 }
00217 }
00218 else
00219 System.err.println("Unknown Code attribute " + a + " ignored.");
00220 }
00221 }
00222 else if(a instanceof ExceptionTable) {
00223 String[] names = ((ExceptionTable)a).getExceptionNames();
00224 for(int j=0; j < names.length; j++)
00225 addException(names[j]);
00226 }
00227 else
00228 addAttribute(a);
00229 }
00230 }
00231
00232
00233
00234
00235
00236
00237
00238 public void addCodeAttribute(Attribute a) { code_attrs_vec.addElement(a); }
00239
00240
00241
00242
00243
00244 public void addException(String class_name) {
00245 throws_vec.addElement(class_name);
00246 }
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258 public CodeExceptionGen addExceptionHandler(InstructionHandle start_pc,
00259 InstructionHandle end_pc,
00260 InstructionHandle handler_pc,
00261 ObjectType catch_type) {
00262 if((start_pc == null) || (end_pc == null) || (handler_pc == null))
00263 throw new ClassGenException("Exception handler target is null instruction");
00264
00265 CodeExceptionGen c = new CodeExceptionGen(start_pc, end_pc,
00266 handler_pc, catch_type);
00267 exception_vec.addElement(c);
00268 return c;
00269 }
00270
00271
00272
00273 public CodeExceptionGen addExceptionHandler(InstructionHandle start_pc,
00274 InstructionHandle end_pc,
00275 InstructionHandle handler_pc,
00276 String catch_type) {
00277 return addExceptionHandler(start_pc, end_pc, handler_pc, catch_type == null?
00278 null : new ObjectType(catch_type));
00279 }
00280
00281
00282
00283
00284
00285
00286
00287 public LineNumberGen addLineNumber(InstructionHandle ih, int src_line) {
00288 LineNumberGen l = new LineNumberGen(ih, src_line);
00289 line_number_vec.addElement(l);
00290 return l;
00291 }
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304 public LocalVariableGen addLocalVariable(String name, Type type, int slot,
00305 InstructionHandle start,
00306 InstructionHandle end) {
00307 byte t = type.getType();
00308 int add = type.getSize();
00309
00310 if(slot + add > max_locals)
00311 max_locals = slot + add;
00312
00313 LocalVariableGen l = new LocalVariableGen(slot, name, type, start, end);
00314 int i;
00315
00316 if((i = variable_vec.indexOf(l)) >= 0)
00317 variable_vec.setElementAt(l, i);
00318 else
00319 variable_vec.addElement(l);
00320 return l;
00321 }
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334 public LocalVariableGen addLocalVariable(String name, Type type,
00335 InstructionHandle start,
00336 InstructionHandle end) {
00337 return addLocalVariable(name, type, max_locals, start, end);
00338 }
00339
00340
00341 public void addObserver(MethodObserver o) {
00342 if(observers == null)
00343 observers = new Vector();
00344
00345 observers.add(o);
00346 }
00347 public String getArgName(int i) { return arg_names[i]; }
00348 public String[] getArgNames() { return (String[])arg_names.clone(); }
00349 public Type getArgType(int i) { return arg_types[i]; }
00350 public Type[] getArgTypes() { return (Type[])arg_types.clone(); }
00351
00352
00353 public String getClassName() { return class_name; }
00354
00355
00356
00357 public Attribute[] getCodeAttributes() {
00358 Attribute[] attributes = new Attribute[code_attrs_vec.size()];
00359 code_attrs_vec.copyInto(attributes);
00360 return attributes;
00361 }
00362
00363
00364
00365 private CodeException[] getCodeExceptions() {
00366 int size = exception_vec.size();
00367 CodeException[] c_exc = new CodeException[size];
00368
00369 try {
00370 for(int i=0; i < size; i++) {
00371 CodeExceptionGen c = (CodeExceptionGen)exception_vec.elementAt(i);
00372 c_exc[i] = c.getCodeException(cp);
00373 }
00374 } catch(ArrayIndexOutOfBoundsException e) {}
00375
00376 return c_exc;
00377 }
00378
00379
00380
00381 public CodeExceptionGen[] getExceptionHandlers() {
00382 CodeExceptionGen[] cg = new CodeExceptionGen[exception_vec.size()];
00383 exception_vec.copyInto(cg);
00384 return cg;
00385 }
00386
00387
00388
00389 public String[] getExceptions() {
00390 String[] e = new String[throws_vec.size()];
00391 throws_vec.copyInto(e);
00392 return e;
00393 }
00394
00395
00396
00397 private ExceptionTable getExceptionTable(ConstantPoolGen cp) {
00398 int size = throws_vec.size();
00399 int[] ex = new int[size];
00400
00401 try {
00402 for(int i=0; i < size; i++)
00403 ex[i] = cp.addClass((String)throws_vec.elementAt(i));
00404 } catch(ArrayIndexOutOfBoundsException e) {}
00405
00406 return new ExceptionTable(cp.addUtf8("Exceptions"),
00407 2 + 2 * size, ex, cp.getConstantPool());
00408 }
00409 public InstructionList getInstructionList() { return il; }
00410
00411
00412
00413 public LineNumberGen[] getLineNumbers() {
00414 LineNumberGen[] lg = new LineNumberGen[line_number_vec.size()];
00415 line_number_vec.copyInto(lg);
00416 return lg;
00417 }
00418
00419
00420
00421 public LineNumberTable getLineNumberTable(ConstantPoolGen cp) {
00422 int size = line_number_vec.size();
00423 LineNumber[] ln = new LineNumber[size];
00424
00425 try {
00426 for(int i=0; i < size; i++)
00427 ln[i] = ((LineNumberGen)line_number_vec.elementAt(i)).getLineNumber(cp);
00428 } catch(ArrayIndexOutOfBoundsException e) {}
00429
00430 return new LineNumberTable(cp.addUtf8("LineNumberTable"),
00431 2 + ln.length * 4, ln, cp.getConstantPool());
00432 }
00433
00434
00435
00436
00437
00438
00439 public LocalVariableGen[] getLocalVariables() {
00440 int size = variable_vec.size();
00441 LocalVariableGen[] lg = new LocalVariableGen[size];
00442 variable_vec.copyInto(lg);
00443
00444 for(int i=0; i < size; i++) {
00445 if(lg[i].getStart() == null)
00446 lg[i].setStart(il.getStart());
00447
00448 if(lg[i].getEnd() == null)
00449 lg[i].setEnd(il.getEnd());
00450 }
00451
00452 if(size > 1)
00453 sort(lg, 0, size - 1);
00454
00455 return lg;
00456 }
00457
00458
00459
00460 public LocalVariableTable getLocalVariableTable(ConstantPoolGen cp) {
00461 LocalVariableGen[] lg = getLocalVariables();
00462 int size = lg.length;
00463 LocalVariable[] lv = new LocalVariable[size];
00464
00465 for(int i=0; i < size; i++)
00466 lv[i] = lg[i].getLocalVariable(cp);
00467
00468 return new LocalVariableTable(cp.addUtf8("LocalVariableTable"),
00469 2 + lv.length * 10, lv, cp.getConstantPool());
00470 }
00471 public int getMaxLocals() { return max_locals; }
00472 public int getMaxStack() { return max_stack; }
00473
00474
00475
00476
00477
00478 public static int getMaxStack(ConstantPoolGen cp, InstructionList il, CodeExceptionGen[] et) {
00479 BranchStack branchTargets = new BranchStack();
00480
00481
00482
00483
00484
00485
00486 for (int i = 0; i < et.length; i++) {
00487 InstructionHandle handler_pc = et[i].getHandlerPC();
00488 if (handler_pc != null)
00489 branchTargets.push(handler_pc, 1);
00490 }
00491
00492 int stackDepth = 0, maxStackDepth = 0;
00493 InstructionHandle ih = il.getStart();
00494
00495 while(ih != null) {
00496 Instruction instruction = ih.getInstruction();
00497 short tag = instruction.getTag();
00498 int delta = instruction.produceStack(cp) - instruction.consumeStack(cp);
00499
00500 stackDepth += delta;
00501 if(stackDepth > maxStackDepth)
00502 maxStackDepth = stackDepth;
00503
00504
00505 if(instruction instanceof BranchInstruction) {
00506 BranchInstruction branch = (BranchInstruction) instruction;
00507 if(instruction instanceof Select) {
00508
00509 Select select = (Select) branch;
00510 InstructionHandle[] targets = select.getTargets();
00511 for (int i = 0; i < targets.length; i++)
00512 branchTargets.push(targets[i], stackDepth);
00513
00514 ih = null;
00515 } else if(!(branch instanceof IfInstruction)) {
00516
00517
00518 if(tag == Constants.JSR || tag == Constants.JSR_W)
00519 branchTargets.push(ih.getNext(), stackDepth - 1);
00520 ih = null;
00521 }
00522
00523
00524
00525 branchTargets.push(branch.getTarget(), stackDepth);
00526 } else {
00527
00528 if(tag == Constants.ATHROW || tag == Constants.RET ||
00529 (tag >= Constants.IRETURN && tag <= Constants.RETURN))
00530 ih = null;
00531 }
00532
00533 if(ih != null)
00534 ih = ih.getNext();
00535
00536 if(ih == null) {
00537 BranchTarget bt = branchTargets.pop();
00538 if (bt != null) {
00539 ih = bt.target;
00540 stackDepth = bt.stackDepth;
00541 }
00542 }
00543 }
00544
00545 return maxStackDepth;
00546 }
00547
00548
00549
00550
00551
00552
00553
00554 public Method getMethod() {
00555 String signature = getSignature();
00556 int name_index = cp.addUtf8(name);
00557 int signature_index = cp.addUtf8(signature);
00558
00559
00560
00561 byte[] byte_code = null;
00562
00563 if(il != null)
00564 byte_code = il.getByteCode();
00565
00566
00567
00568 if((variable_vec.size() > 0) && !strip_attributes)
00569 addCodeAttribute(getLocalVariableTable(cp));
00570
00571 if((line_number_vec.size() > 0) && !strip_attributes)
00572 addCodeAttribute(getLineNumberTable(cp));
00573
00574 Attribute[] code_attrs = getCodeAttributes();
00575
00576
00577
00578 int attrs_len = 0;
00579 for(int i=0; i < code_attrs.length; i++)
00580 attrs_len += (code_attrs[i].getLength() + 6);
00581
00582 CodeException[] c_exc = getCodeExceptions();
00583 int exc_len = c_exc.length * 8;
00584
00585 if((il != null) && !isAbstract()) {
00586 Code code = new Code(cp.addUtf8("Code"),
00587 8 + byte_code.length +
00588 2 + exc_len +
00589 2 + attrs_len,
00590 max_stack, max_locals,
00591 byte_code, c_exc,
00592 code_attrs,
00593 cp.getConstantPool());
00594
00595 addAttribute(code);
00596 }
00597
00598 if(throws_vec.size() > 0)
00599 addAttribute(getExceptionTable(cp));
00600
00601 return new Method(access_flags, name_index, signature_index,
00602 getAttributes(), cp.getConstantPool());
00603 }
00604
00605
00606 public String getMethodName() { return getName(); }
00607
00608
00609 public String getMethodSignature() {
00610 return getSignature();
00611 }
00612 public Type getReturnType() { return getType(); }
00613 public String getSignature() {
00614 return Type.getMethodSignature(type, arg_types);
00615 }
00616
00617
00618
00619 public void removeCodeAttribute(Attribute a) { code_attrs_vec.removeElement(a); }
00620
00621
00622
00623 public void removeException(String c) {
00624 throws_vec.removeElement(c);
00625 }
00626
00627
00628
00629 public void removeExceptionHandler(CodeExceptionGen c) {
00630 exception_vec.removeElement(c);
00631 }
00632
00633
00634
00635 public void removeExceptionHandlers() {
00636 exception_vec.clear();
00637 }
00638
00639
00640
00641 public void removeExceptions() {
00642 throws_vec.clear();
00643 }
00644
00645
00646
00647 public void removeLineNumber(LineNumberGen l) {
00648 line_number_vec.removeElement(l);
00649 }
00650
00651
00652
00653 public void removeLineNumbers() {
00654 line_number_vec.clear();
00655 }
00656
00657
00658
00659
00660 public void removeLocalVariable(LocalVariableGen l) {
00661 variable_vec.removeElement(l);
00662 }
00663
00664
00665
00666 public void removeLocalVariables() {
00667 variable_vec.clear();
00668 }
00669
00670
00671
00672
00673
00674 public void removeNOPs() {
00675 if(il != null) {
00676 InstructionHandle next;
00677
00678
00679 for(InstructionHandle ih = il.getStart(); ih != null; ih = next) {
00680 next = ih.next;
00681
00682 if((next != null) && (ih.getInstruction() instanceof NOP)) {
00683 try {
00684 il.delete(ih);
00685 } catch(TargetLostException e) {
00686 InstructionHandle[] targets = e.getTargets();
00687
00688 for(int i=0; i < targets.length; i++) {
00689 InstructionTargeter[] targeters = targets[i].getTargeters();
00690
00691 for(int j=0; j < targeters.length; j++)
00692 targeters[j].updateTarget(targets[i], next);
00693 }
00694 }
00695 }
00696 }
00697 }
00698 }
00699
00700
00701 public void removeObserver(MethodObserver o) {
00702 if(observers != null)
00703 observers.removeElement(o);
00704 }
00705 public void setArgName(int i, String name) { arg_names[i] = name; }
00706 public void setArgNames(String[] arg_names) { this.arg_names = arg_names; }
00707 public void setArgType(int i, Type type) { arg_types[i] = type; }
00708 public void setArgTypes(Type[] arg_types) { this.arg_types = arg_types; }
00709 public void setClassName(String class_name) { this.class_name = class_name; }
00710 public void setInstructionList(InstructionList il) { this.il = il; }
00711
00712
00713
00714 public void setMaxLocals() {
00715 if(il != null) {
00716 int max = 0;
00717 LocalVariableInstruction last_local = null;
00718
00719 if(arg_types != null)
00720 for(int i=0; i < arg_types.length; i++)
00721 max += arg_types[i].getSize();
00722
00723 for(InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
00724 Instruction ins = ih.getInstruction();
00725 if(ins instanceof LocalVariableInstruction) {
00726 LocalVariableInstruction local = (LocalVariableInstruction)ins;
00727 int index = local.getIndex() + local.getType(cp).getSize();
00728
00729 if(index > max)
00730 max = index;
00731 }
00732 }
00733
00734 max_locals = max;
00735 } else
00736 max_locals = 0;
00737 }
00738
00739
00740
00741 public void setMaxLocals(int m) { max_locals = m; }
00742
00743
00744
00745
00746 public void setMaxStack() {
00747 if(il != null)
00748 max_stack = getMaxStack(cp, il, getExceptionHandlers());
00749 else
00750 max_stack = 0;
00751 }
00752
00753
00754
00755 public void setMaxStack(int m) { max_stack = m; }
00756
00757
00758 public void setMethodName(String method_name) { setName(method_name); }
00759 public void setReturnType(Type return_type) { setType(return_type); }
00760
00761
00762
00763 private static final void sort(LocalVariableGen[] vars, int l, int r) {
00764 int i = l, j = r;
00765 int m = vars[(l + r) / 2].getIndex();
00766 LocalVariableGen h;
00767
00768 do {
00769 while(vars[i].getIndex() < m) i++;
00770 while(m < vars[j].getIndex()) j--;
00771
00772 if(i <= j) {
00773 h=vars[i]; vars[i]=vars[j]; vars[j]=h;
00774 i++; j--;
00775 }
00776 } while(i <= j);
00777
00778 if(l < j) sort(vars, l, j);
00779 if(i < r) sort(vars, i, r);
00780 }
00781
00782
00783
00784 public void stripAttributes(boolean flag) { strip_attributes = flag; }
00785
00786
00787
00788
00789
00790
00791 public final String toString() {
00792 String access = Utility.accessToString(access_flags);
00793 String signature = Type.getMethodSignature(type, arg_types);
00794
00795 signature = Utility.methodSignatureToString(signature, name, access);
00796
00797 StringBuffer buf = new StringBuffer(signature);
00798
00799 if(throws_vec.size() > 0) {
00800 for(Enumeration e = throws_vec.elements(); e.hasMoreElements(); )
00801 buf.append("\n\t\tthrows " + e.nextElement());
00802 }
00803
00804 return buf.toString();
00805 }
00806
00807
00808
00809
00810 public void update() {
00811 if(observers != null)
00812 for(Enumeration e = observers.elements(); e.hasMoreElements(); )
00813 ((MethodObserver)e.nextElement()).notify(this);
00814 }
00815 }