sort of working
[org.ibex.classgen.git] / src / org / ibex / classgen / MethodGen.java
1 package org.ibex.classgen;
2
3 import java.io.*;
4
5 public class MethodGen implements CGConst {
6     private final static boolean EMIT_NOPS = true;
7     
8     private final CPGen cp;
9     private final String name;
10     private final Type ret;
11     private final Type[] args;
12     private final int flags;
13     private final AttrGen attrs;
14     private final AttrGen codeAttrs;
15     
16     private final int nameIndex;
17     private final int descriptorIndex;
18     
19     private int maxStack = 16;
20     private int maxLocals=1;
21     
22     private int size;
23     private int capacity;
24     private byte[] op;
25     private Object[] arg;
26     
27     MethodGen(ClassGen owner, String name, Type ret, Type[] args, int flags) {
28         if((flags & ~(ACC_PUBLIC|ACC_PRIVATE|ACC_PROTECTED|ACC_STATIC|ACC_FINAL|ACC_SYNCHRONIZED|ACC_NATIVE|ACC_ABSTRACT|ACC_STRICT)) != 0)
29             throw new IllegalArgumentException("invalid flags");
30         this.cp = owner.cp;
31         this.name = name;
32         this.ret = ret;
33         this.args = args;
34         this.flags = flags;
35         
36         attrs = new AttrGen(cp);
37         codeAttrs = new AttrGen(cp);
38         
39         nameIndex = cp.addUtf8(name).index;
40         descriptorIndex = cp.addUtf8(descriptor()).index;
41         
42         if((owner.flags & ACC_INTERFACE) != 0 || (flags & (ACC_ABSTRACT|ACC_NATIVE)) != 0) size = capacity = -1;
43     }
44     
45     public String descriptor() { return descriptor(ret,args); }
46     public static String descriptor(Type ret, Type[] args) {
47         StringBuffer sb = new StringBuffer(args.length*4);
48         sb.append("(");
49         for(int i=0;i<args.length;i++) sb.append(args[i].getDescriptor());
50         sb.append(")");
51         sb.append(ret.getDescriptor());
52         return sb.toString();
53     }
54         
55     private final void grow() { if(size == capacity) grow(size+1); }
56     private final void grow(int newCap) {
57         if(newCap <= capacity) return;
58         newCap = Math.max(newCap,capacity == 0 ? 256 : capacity*2);
59         
60         byte[] op2 = new byte[newCap];
61         if(capacity != 0) System.arraycopy(op,0,op2,0,size);
62         op = op2;
63         
64         Object[] arg2 = new Object[newCap];
65         if(capacity != 0) System.arraycopy(arg,0,arg2,0,size);
66         arg = arg2;
67         
68         capacity = newCap;
69     }
70     public final int size() { return size; }
71     
72     public final int addPushConst(int n) { grow(); setPushConst(size,n); return size++; }
73     public final int addPushConst(long n) { grow(); setPushConst(size,n); return size++; }
74     public final int addPushConst(float n) { grow(); setPushConst(size,n); return size++; }
75     public final int addPushConst(double n) { grow(); setPushConst(size,n); return size++; }
76     public final int addPushConst(Object o) { grow(); setPushConst(size,o); return size++; }
77     public final int add(byte op, int arg) { return add(op,N(arg)); }
78     public final int add(byte op, Object arg) { grow(); set(size,op,arg); return size++; }
79     public final int add(byte op) { return add(op,null); }
80         
81     public final void set(int pos, byte op) { set(pos,op,null); }
82     public final void set(int pos, byte op, int n) { set(pos,op,N(n)); }
83     public final void set(int pos, byte op, Object arg) {
84         if(capacity == -1) throw new IllegalStateException("method can't have code");
85         if(size == -1) throw new IllegalStateException("method is finalized");
86         int iarg = arg instanceof Integer ? ((Integer)arg).intValue() : -1;
87         
88         switch(op) {
89             case LDC: if(iarg >= 256) op = LDC_W; break;
90         }
91         this.op[pos] = op;
92         this.arg[pos] = arg;
93     }
94         
95     public final void setPushConst(int pos, int n) {
96         switch(n) {
97             case -1: set(pos,ICONST_M1); break;
98             case 0:  set(pos,ICONST_0);  break;
99             case 1:  set(pos,ICONST_1);  break;
100             case 2:  set(pos,ICONST_2);  break; 
101             case 3:  set(pos,ICONST_3);  break;
102             case 4:  set(pos,ICONST_4);  break;
103             case 5:  set(pos,ICONST_5);  break;
104             default:
105                 if(n >= -128 && n <= 127) set(pos,BIPUSH,n);
106                 if(n >= -32767 && n <= 32767) set(pos,SIPUSH,n);
107                 setLDC(pos,N(n));
108                 break;
109         }
110     }
111     public final void setPushConst(int pos, long l) {
112         if(l==0) set(pos,LCONST_0);
113         else if(l==1) set(pos,LCONST_1);
114         else setLDC(pos,N(l));
115     }
116     
117     public final void setPushConst(int pos, float f) {
118         if(f == 1.0f) set(pos,FCONST_0);
119         else if(f == 1.0f) set(pos,FCONST_1);
120         else if(f == 2.0f) set(pos,FCONST_2);
121         else setLDC(pos,N(f));
122     }
123     public final void setPushConst(int pos, double d) {
124         if(d == 1.0) set(pos,DCONST_0);
125         else if(d == 2.0) set(pos,DCONST_1);
126         else setLDC(pos,N(d));
127     }
128     public final void setPushConst(int pos, Object o) {
129         if(o instanceof Integer) setPushConst(pos,((Integer)o).intValue());
130         else if(o instanceof Long) setPushConst(pos,((Long)o).longValue());
131         else if(o instanceof Float) setPushConst(pos,((Float)o).floatValue());
132         else if(o instanceof Double) setPushConst(pos,((Double)o).doubleValue());
133         else setLDC(pos,o);
134     }
135         
136     private void setLDC(int pos, Object o) { set(pos,LDC,cp.add(o)); }
137
138     public final CPGen.Ent methodRef(Type.Object c, String name, Type ret, Type[] args) {        
139         return methodRef(c,name,MethodGen.descriptor(ret,args));
140     }
141     public final CPGen.Ent methodRef(Type.Object c, String name, String descriptor) {
142         return cp.add(new CPGen.MethodRef(c,new CPGen.NameAndType(name,descriptor)));
143     }
144     public final CPGen.Ent fieldRef(Type.Object c, String name, Type type) {
145         return fieldRef(c,name,type.getDescriptor());
146     }
147     public final CPGen.Ent fieldRef(Type.Object c, String name, String descriptor) {
148         return cp.add(new CPGen.FieldRef(c,new CPGen.NameAndType(name,descriptor)));
149     }    
150     
151     public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; }
152     public void setMaxStack(int maxStack) { this.maxStack = maxStack; }
153     
154     public void finish() {
155         try {
156             _finish();
157         } catch(IOException e) {
158             throw new Error("should never happen");
159         }
160     }
161     
162     private void _finish() throws IOException {
163         if(size == -1) return;
164         
165         ByteArrayOutputStream baos = new ByteArrayOutputStream();
166         DataOutput o = new DataOutputStream(baos);
167     
168         int[] pc = new int[size];
169         int[] maxpc = pc;
170         int p,i;
171         
172         // Pass1 - Calculate maximum pc of each bytecode
173         for(i=0,p=0;i<size;i++) {
174             byte op = this.op[i];
175             maxpc[i] = p;
176             switch(op) {
177                 case GOTO: p += 3; break;
178                 case JSR: p += 3; break;
179                 case NOP: if(!EMIT_NOPS) continue; /* fall though */
180                 default: p += 1 + opArgLength(op); break;
181             }
182         }
183         // Pass2 - Widen instructions if they can possibly be too short
184         for(i=0;i<size;i++) {
185             switch(op[i]) {
186                 case GOTO:
187                 case JSR: {
188                     int arg = ((Integer)this.arg[i]).intValue();
189                     int diff = maxpc[arg] - maxpc[i];
190                     if(diff < -32768 || diff > 32767)
191                         op[i] = op[i] == GOTO ? GOTO_W : JSR_W;
192                 }
193             }
194         }
195         // Pass3 - Calculate actual pc
196         for(i=0,p=0;i<size;i++) {
197             byte op = this.op[i];
198             pc[i] = p;
199             p += op != NOP || EMIT_NOPS ? 1 + opArgLength(op) : 0;
200         }
201         
202         o.writeShort(maxStack);
203         o.writeShort(maxLocals);
204         o.writeInt(p);
205         
206         // Pass 4 - Actually write the bytecodes
207         for(i=0;i<size;i++) {
208             byte op = this.op[i];
209             System.err.println("" + i + " Writing " + Integer.toHexString(op) + " at " + pc[i]);
210             if(op == NOP && !EMIT_NOPS) continue;
211             int argLength = opArgLength(op);
212             o.writeByte(op&0xff);
213             if(argLength == 0) continue;
214             Object arg = this.arg[i];            
215             int iarg = arg instanceof Integer ? ((Integer)arg).intValue() : -1;
216             int outArg = iarg;
217             
218             switch(op) {
219                 case IINC: {
220                     int[] pair = (int[]) arg;
221                     if(pair[0] > 255 || pair[1] < -128 || pair[1] > 127) throw new ClassGen.Exn("overflow of iinc arg"); 
222                     o.writeByte(pair[0]);
223                     o.writeByte(pair[1]);
224                     continue;
225                 }
226                 case IF_ICMPLT:
227                 case GOTO:
228                 case GOTO_W:
229                 case JSR:
230                 case JSR_W:
231                     outArg = pc[iarg] - pc[i];
232                     if(outArg < -32768 || outArg > 32767) throw new ClassGen.Exn("overflow of s2 offset");
233                     break;
234                 case INVOKESTATIC:
235                 case INVOKESPECIAL:
236                 case INVOKEVIRTUAL:
237                 case GETSTATIC:
238                 case PUTSTATIC:
239                 case GETFIELD:
240                 case PUTFIELD:
241                 case LDC_W:
242                 case LDC:
243                     outArg = ((CPGen.Ent)arg).index;
244                     break;
245             }
246             
247             if(argLength == 1) o.writeByte(outArg);
248             else if(argLength == 2) o.writeShort(outArg);
249             else throw new Error("should never happen");
250         }
251
252         o.writeShort(0); // FIXME: Exception table
253         o.writeShort(codeAttrs.size());
254         codeAttrs.dump(o);
255         
256         baos.close();
257         
258         byte[] codeAttribute = baos.toByteArray();
259         attrs.add("Code",codeAttribute);
260         size = -1;        
261     }
262         
263     public void dump(DataOutput o) throws IOException {
264         o.writeShort(flags);
265         o.writeShort(nameIndex);
266         o.writeShort(descriptorIndex);
267         o.writeShort(attrs.size());
268         attrs.dump(o);
269     }
270     
271     private static int opArgLength(byte op) {
272         switch(op) {
273             case NOP:
274             case ICONST_M1:
275             case ICONST_0:
276             case ICONST_1:
277             case ICONST_2:
278             case ICONST_3:
279             case ICONST_4:
280             case ICONST_5:
281             case LCONST_0:
282             case LCONST_1:
283             case FCONST_0:
284             case FCONST_1:
285             case FCONST_2:
286             case DCONST_0:
287             case DCONST_1:
288             case ILOAD_0:
289             case ILOAD_1:
290             case ILOAD_2:
291             case ILOAD_3:
292             case ISTORE_0:
293             case ISTORE_1:
294             case ISTORE_2:
295             case ISTORE_3:
296             case RETURN:
297                 return 0;
298             case LDC:
299             case BIPUSH:
300             case ILOAD:
301             case ISTORE:
302                 return 1;
303             case LDC_W:
304             case SIPUSH:
305             case GETSTATIC:
306             case PUTSTATIC:
307             case GETFIELD:
308             case PUTFIELD:
309             case INVOKESTATIC:
310             case INVOKEVIRTUAL:
311             case INVOKESPECIAL:
312             case IINC:
313             case GOTO:
314             case JSR:
315             case IF_ICMPLT:
316                 return 2;
317             default:
318                 throw new ClassGen.Exn("unknown bytecode " + Integer.toHexString(op&0xff));
319         }
320     }
321         
322     private static Integer N(int n) { return new Integer(n); }
323     private static Long N(long n) { return new Long(n); }
324     private static Float N(float f) { return new Float(f); }
325     private static Double N(double d) { return new Double(d); }
326     private static int max(int a, int b) { return a > b ? a : b; }
327 }