View Javadoc

1   // jq_Method.java, created Mon Feb  5 23:23:20 2001 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
4   package joeq.Class;
5   
6   import java.util.Arrays;
7   import java.util.HashMap;
8   import java.util.Map;
9   import java.io.DataInput;
10  import java.io.DataOutput;
11  import java.io.IOException;
12  import joeq.ClassLib.ClassLibInterface;
13  import joeq.Compiler.BytecodeAnalysis.Bytecodes;
14  import joeq.Main.jq;
15  import joeq.UTF.Utf8;
16  import jwutil.util.Assert;
17  import jwutil.util.Convert;
18  
19  /*
20   * @author  John Whaley <jwhaley@alum.mit.edu>
21   * @version $Id: jq_Method.java 2465 2006-06-07 23:03:17Z joewhaley $
22   */
23  public abstract class jq_Method extends jq_Member {
24      
25      // Available after loading
26      protected char max_stack;
27      protected char max_locals;
28      protected jq_Class[] thrown_exceptions_table;
29      protected byte[] bytecode;
30      protected jq_TryCatchBC[] exception_table;
31      protected jq_LineNumberBC[] line_num_table;
32      protected jq_LocalVarTableEntry[] localvar_table;
33      protected Map codeattribMap;
34      protected jq_Type[] param_types;
35      protected jq_Type return_type;
36      protected int param_words;
37      
38      // Available after compilation
39      protected jq_CompiledCode default_compiled_version;
40      
41      // inherited: clazz, name, desc, access_flags, attributes
42      protected jq_Method(jq_Class clazz, jq_NameAndDesc nd) {
43          super(clazz, nd);
44          parseMethodSignature();
45      }
46      
47      public final void load(jq_Method that) {
48          this.access_flags = that.access_flags;
49          this.max_stack = that.max_stack;
50          this.max_locals = that.max_locals;
51          this.bytecode = that.bytecode;
52          this.exception_table = that.exception_table;
53          this.line_num_table = that.line_num_table;
54          this.codeattribMap = that.codeattribMap;
55          this.attributes = new HashMap();
56          state = STATE_LOADED;
57      }
58      
59      public final void load(char access_flags, char max_stack, char max_locals, byte[] bytecode,
60                             jq_TryCatchBC[] exception_table, jq_LineNumberBC[] line_num_table,
61                             Map codeattribMap) {
62          this.access_flags = access_flags;
63          this.max_stack = max_stack;
64          this.max_locals = max_locals;
65          this.bytecode = bytecode;
66          this.exception_table = exception_table;
67          this.line_num_table = line_num_table;
68          this.codeattribMap = codeattribMap;
69          this.attributes = new HashMap();
70          state = STATE_LOADED;
71          if (jq.RunningNative) {
72              if (this instanceof jq_Initializer) {
73                  ClassLibInterface.DEFAULT.initNewConstructor((java.lang.reflect.Constructor)this.getJavaLangReflectMemberObject(), (jq_Initializer)this);
74              } else {
75                  ClassLibInterface.DEFAULT.initNewMethod((java.lang.reflect.Method)this.getJavaLangReflectMemberObject(), this);
76              }
77          }
78      }
79  
80      public final void load(char access_flags, Map attributes) throws ClassFormatError {
81          super.load(access_flags, attributes);
82          parseAttributes();
83      }
84      
85      public final void load(char access_flags, DataInput in)
86      throws IOException, ClassFormatError {
87          super.load(access_flags, in);
88          parseAttributes();
89      }
90      
91      private final jq_Class getResolvedClassFromCP(char cpidx) {
92      if (clazz.getCPtag(cpidx) != CONSTANT_ResolvedClass)
93          throw new ClassFormatError();
94      jq_Type class_type = clazz.getCPasType(cpidx);
95      if (!class_type.isClassType())
96          throw new ClassFormatError();
97      return (jq_Class)class_type;
98      }
99  
100     private final void parseAttributes() throws ClassFormatError {
101         // parse attributes
102         byte[] a = getAttribute("Code");
103         if (a != null) {
104             max_stack = Convert.twoBytesToChar(a, 0);
105             max_locals = Convert.twoBytesToChar(a, 2);
106             int bytecode_length = Convert.fourBytesToInt(a, 4);
107             if (bytecode_length <= 0)
108                 throw new ClassFormatError();
109             bytecode = new byte[bytecode_length];
110             System.arraycopy(a, 8, bytecode, 0, bytecode_length);
111             int ex_table_length = Convert.twoBytesToChar(a, 8+bytecode_length);
112             exception_table = new jq_TryCatchBC[ex_table_length];
113             int idx = 10+bytecode_length;
114             for (int i=0; i<ex_table_length; ++i) {
115                 char start_pc = Convert.twoBytesToChar(a, idx);
116                 char end_pc = Convert.twoBytesToChar(a, idx+2);
117                 char handler_pc = Convert.twoBytesToChar(a, idx+4);
118                 char catch_cpidx = Convert.twoBytesToChar(a, idx+6);
119                 idx += 8;
120                 jq_Class catch_class = null;
121                 if (catch_cpidx != 0) {
122             catch_class = getResolvedClassFromCP(catch_cpidx);
123                 }
124                 exception_table[i] = new jq_TryCatchBC(start_pc, end_pc, handler_pc, catch_class);
125             }
126             int attrib_count = Convert.twoBytesToChar(a, idx);
127             codeattribMap = new HashMap();
128             idx += 2;
129             for (int i=0; i<attrib_count; ++i) {
130                 char name_index = Convert.twoBytesToChar(a, idx);
131                 if (clazz.getCPtag(name_index) != CONSTANT_Utf8)
132                     throw new ClassFormatError();
133                 Utf8 attribute_desc = (Utf8)clazz.getCPasUtf8(name_index);
134                 int attribute_length = Convert.fourBytesToInt(a, idx+2);
135                 // todo: maybe we only want to parse attributes we care about...
136                 byte[] attribute_data = new byte[attribute_length];
137                 System.arraycopy(a, idx+6, attribute_data, 0, attribute_length);
138                 codeattribMap.put(attribute_desc, attribute_data);
139                 idx += 6 + attribute_length;
140             }
141             if (idx != a.length) {
142                 throw new ClassFormatError();
143             }
144             a = getCodeAttribute(Utf8.get("LineNumberTable"));
145             if (a != null) {
146                 char num_of_line_attribs = Convert.twoBytesToChar(a, 0);
147                 if (a.length != (num_of_line_attribs*4+2))
148                     throw new ClassFormatError();
149                 this.line_num_table = new jq_LineNumberBC[num_of_line_attribs];
150                 for (int i=0; i<num_of_line_attribs; ++i) {
151                     char start_pc = Convert.twoBytesToChar(a, i*4+2);
152                     char line_number = Convert.twoBytesToChar(a, i*4+4);
153                     this.line_num_table[i] = new jq_LineNumberBC(start_pc, line_number);
154                 }
155                 Arrays.sort(this.line_num_table);
156             } else {
157                 this.line_num_table = new jq_LineNumberBC[0];
158             }
159             a = getCodeAttribute(Utf8.get("LocalVariableTable"));
160             if (a != null) {
161                 char num_of_local_vars = Convert.twoBytesToChar(a, 0);
162                 if (a.length != (num_of_local_vars*10+2))
163                     throw new ClassFormatError();
164 
165                 this.localvar_table = new jq_LocalVarTableEntry[num_of_local_vars];
166                 for (int i=0; i<num_of_local_vars; ++i) {
167                     char start_pc = Convert.twoBytesToChar(a, i*10+2);
168                     char length = Convert.twoBytesToChar(a, i*10+4);
169                     char name_index = Convert.twoBytesToChar(a, i*10+6);
170                     if (clazz.getCPtag(name_index) != CONSTANT_Utf8)
171                         throw new ClassFormatError();
172                     char desc_index = Convert.twoBytesToChar(a, i*10+8);
173                     if (clazz.getCPtag(desc_index) != CONSTANT_Utf8)
174                         throw new ClassFormatError();
175                     char index = Convert.twoBytesToChar(a, i*10+10);
176                     this.localvar_table[i] = new jq_LocalVarTableEntry(start_pc, length, new jq_NameAndDesc(clazz.getCPasUtf8(name_index), clazz.getCPasUtf8(desc_index)), index);
177                 }
178                 Arrays.sort(this.localvar_table);
179             }
180         } else {
181             if (!isNative() && !isAbstract())
182                 throw new ClassFormatError();
183         }
184         // read thrown Exceptions
185         a = getAttribute("Exceptions");
186         if (a != null) {
187             char number_of_thrown_exceptions = Convert.twoBytesToChar(a, 0);
188         thrown_exceptions_table = new jq_Class[number_of_thrown_exceptions];
189         for (int i = 0; i < number_of_thrown_exceptions; i++) {
190         char cpidx = Convert.twoBytesToChar(a, 2 + 2*i);
191         thrown_exceptions_table[i] = getResolvedClassFromCP(cpidx);
192         }
193     }
194         state = STATE_LOADED;
195         if (jq.RunningNative) {
196             if (this instanceof jq_Initializer) {
197                 ClassLibInterface.DEFAULT.initNewConstructor((java.lang.reflect.Constructor)this.getJavaLangReflectMemberObject(), (jq_Initializer)this);
198             } else {
199                 ClassLibInterface.DEFAULT.initNewMethod((java.lang.reflect.Method)this.getJavaLangReflectMemberObject(), this);
200             }
201         }
202     }
203 
204     public Bytecodes.CodeException[] getExceptionTable(Bytecodes.InstructionList il) {
205         Bytecodes.CodeException[] ex_table = new Bytecodes.CodeException[exception_table.length];
206         for (int i=0; i<ex_table.length; ++i) {
207             ex_table[i] = new Bytecodes.CodeException(il, bytecode, exception_table[i]);
208         }
209         return ex_table;
210     }
211     
212     public Bytecodes.LineNumber[] getLineNumberTable(Bytecodes.InstructionList il) {
213         Bytecodes.LineNumber[] line_num = new Bytecodes.LineNumber[line_num_table.length];
214         for (int i=0; i<line_num.length; ++i) {
215             line_num[i] = new Bytecodes.LineNumber(il, line_num_table[i]);
216         }
217         return line_num;
218     }
219     
220     public void setCode(Bytecodes.InstructionList il,
221                        Bytecodes.CodeException[] ex_table,
222                        Bytecodes.LineNumber[] line_num,
223                        jq_ConstantPool.ConstantPoolRebuilder cpr) {
224         cpr.resetIndices(il);
225         bytecode = il.getByteCode();
226         if (exception_table.length != ex_table.length)
227             exception_table = new jq_TryCatchBC[ex_table.length];
228         // TODO: recalculate max_stack and max_locals.
229         for (int i=0; i<ex_table.length; ++i) {
230             exception_table[i] = ex_table[i].finish();
231         }
232         if (line_num_table.length != line_num.length)
233             line_num_table = new jq_LineNumberBC[line_num.length];
234         for (int i=0; i<line_num.length; ++i) {
235             line_num_table[i] = line_num[i].finish();
236         }
237     }
238     
239     public void update(jq_ConstantPool.ConstantPoolRebuilder cpr) {
240         if (bytecode != null) {
241             Bytecodes.InstructionList il = new Bytecodes.InstructionList(getDeclaringClass().getCP(), bytecode);
242             Bytecodes.CodeException[] ex_table = getExceptionTable(il);
243             Bytecodes.LineNumber[] line_num = getLineNumberTable(il);
244             setCode(il, ex_table, line_num, cpr);
245         }
246     }
247     
248     public void remakeCodeAttribute(jq_ConstantPool.ConstantPoolRebuilder cpr) {
249         if (bytecode != null) {
250             // TODO: include line number table.
251             int size = 8 + bytecode.length + 2 + 8*exception_table.length + 2;
252             byte[] code = new byte[size];
253             Convert.charToTwoBytes(max_stack, code, 0);
254             Convert.charToTwoBytes(max_locals, code, 2);
255             Convert.intToFourBytes(bytecode.length, code, 4);
256             System.arraycopy(bytecode, 0, code, 8, bytecode.length);
257             Convert.charToTwoBytes((char)exception_table.length, code, 8+bytecode.length);
258             int idx = 10+bytecode.length;
259             for (int i=0; i<exception_table.length; ++i) {
260                 Convert.charToTwoBytes(exception_table[i].getStartPC(), code, idx);
261                 Convert.charToTwoBytes(exception_table[i].getEndPC(), code, idx+2);
262                 Convert.charToTwoBytes(exception_table[i].getHandlerPC(), code, idx+4);
263                 char c;
264                 if (exception_table[i].getExceptionType() == null) c = (char) 0;
265                 else c = cpr.get(exception_table[i].getExceptionType());
266                 Convert.charToTwoBytes(c, code, idx+6);
267                 idx += 8;
268             }
269             char attrib_count = (char)0; // TODO: code attributes
270             // TODO: LocalVariableTable
271             Convert.charToTwoBytes(attrib_count, code, idx);
272             attributes.put(Utf8.get("Code"), code);
273         }
274     }
275     
276     public void dumpAttributes(DataOutput out, jq_ConstantPool.ConstantPoolRebuilder cpr) throws IOException {
277         update(cpr);
278         remakeCodeAttribute(cpr);
279         // TODO: Exceptions
280         super.dumpAttributes(out, cpr);
281     }
282 
283     public abstract void prepare();
284 
285     static interface Delegate {
286         jq_CompiledCode compile_stub(jq_Method m);
287         jq_CompiledCode compile(jq_Method m);
288     }
289 
290     private static Delegate _delegate;
291 
292     public final jq_CompiledCode compile_stub() {
293         chkState(STATE_PREPARED);
294         if (state >= STATE_SFINITIALIZED) return default_compiled_version;
295         if (jq.DontCompile) {
296             state = STATE_SFINITIALIZED;
297             return default_compiled_version = new jq_CompiledCode(this, null, 0, null, null, null, null, 0, null, null);
298         }
299         if (_compile.getState() < STATE_CLSINITIALIZED) _compile.compile();
300         default_compiled_version = _delegate.compile_stub(this);
301         state = STATE_SFINITIALIZED;
302         return default_compiled_version;
303     }
304     public final jq_CompiledCode compile() {
305         if (state == STATE_CLSINITIALIZED) return default_compiled_version;
306         synchronized (this) {
307             Assert._assert(!jq.DontCompile);
308             chkState(STATE_PREPARED);
309             default_compiled_version = _delegate.compile(this);
310             state = STATE_CLSINITIALIZED;
311         }
312         return default_compiled_version;
313     }
314     
315     public final void setDefaultCompiledVersion(jq_CompiledCode cc) {
316         default_compiled_version = cc;
317     }
318     
319     public final int getReturnWords() {
320         if (return_type == jq_Primitive.VOID) return 0;
321         if (return_type == jq_Primitive.LONG ||
322             return_type == jq_Primitive.DOUBLE) return 2;
323         return 1;
324     }
325     
326     protected abstract void parseMethodSignature();
327     public final boolean isSynchronized() { return checkAccessFlag(ACC_SYNCHRONIZED); }
328     public final boolean isNative() { return checkAccessFlag(ACC_NATIVE); }
329     public final boolean isAbstract() { return checkAccessFlag(ACC_ABSTRACT); }
330     public final boolean isStrict() { return checkAccessFlag(ACC_STRICT); }
331     public final jq_CompiledCode getDefaultCompiledVersion() { chkState(STATE_SFINITIALIZED); return default_compiled_version; }
332     public char getMaxStack() {
333         chkState(STATE_LOADED);
334         Assert._assert(getBytecode() != null);
335         return max_stack;
336     }
337     public void setMaxStack(char m) {
338         this.max_stack = m;
339     }
340     public char getMaxLocals() {
341         chkState(STATE_LOADED);
342         Assert._assert(getBytecode() != null);
343         return max_locals;
344     }
345     public void setMaxLocals(char m) {
346         this.max_locals = m;
347     }
348     public byte[] getBytecode() {
349         chkState(STATE_LOADED);
350         return bytecode;
351     }
352     public jq_TryCatchBC[] getExceptionTable() {
353         chkState(STATE_LOADED);
354         Assert._assert(getBytecode() != null);
355         return exception_table;
356     }
357     public jq_Class[] getThrownExceptionsTable() { 
358         chkState(STATE_LOADED);
359     return thrown_exceptions_table; 
360     }
361     public jq_LocalVarTableEntry getLocalVarTableEntry(int bci, int index) {
362         if (localvar_table == null)
363             return null;
364         int inspoint = Arrays.binarySearch(localvar_table, new jq_LocalVarTableEntry((char)bci, (char)index));
365         if (inspoint >= 0)
366             return localvar_table[inspoint];
367         inspoint = -inspoint-2;
368         if(inspoint >= 0 && localvar_table[inspoint].isInRange(bci, index))
369             return localvar_table[inspoint];
370         return null;
371     }
372     public int getLineNumber(int bci) {
373         // todo: binary search
374         if (line_num_table == null)
375             return -1;
376         for (int i=line_num_table.length-1; i>=0; --i) {
377             if (bci >= line_num_table[i].getStartPC()) return line_num_table[i].getLineNum();
378         }
379         return -1;
380     }
381     public jq_LineNumberBC[] getLineNumberTable() { return line_num_table; }
382     public jq_Type[] getParamTypes() { return param_types; }
383     public int getParamWords() { return param_words; }
384     public final jq_Type getReturnType() { return return_type; }
385     public byte[] getCodeAttribute(Utf8 a) { chkState(STATE_LOADING2); return (byte[])codeattribMap.get(a); }
386     public final byte[] getCodeAttribute(String name) { return getCodeAttribute(Utf8.get(name)); }
387 
388     public jq_LineNumberBC getLineNumber(char linenum) {
389         // todo: binary search
390         jq_LineNumberBC[] ln = getLineNumberTable();
391         if (ln == null)
392             return null;
393         for (int i = 0; i < ln.length; ++i) {
394             if (ln[i].getLineNum() == linenum)
395                 return ln[i];
396         }
397         return null;
398     }
399 
400     public void accept(jq_MethodVisitor mv) {
401         mv.visitMethod(this);
402     }
403     
404     public String toString() { return getDeclaringClass()+"."+nd; }
405 
406     public boolean isBodyLoaded() {
407         return getBytecode()!=null;
408     }
409     
410     public static final jq_Class _class;
411     public static final jq_InstanceMethod _compile;
412     public static final jq_InstanceField _default_compiled_version;
413     static {
414         _class = (jq_Class)PrimordialClassLoader.loader.getOrCreateBSType("Ljoeq/Class/jq_Method;");
415         _compile = _class.getOrCreateInstanceMethod("compile", "()Ljoeq/Class/jq_CompiledCode;");
416         _default_compiled_version = _class.getOrCreateInstanceField("default_compiled_version", "Ljoeq/Class/jq_CompiledCode;");
417         /* Set up delegates. */
418         _delegate = null;
419         boolean nullVM = jq.nullVM;
420         if (!nullVM) {
421             _delegate = attemptDelegate("joeq.Class.Delegates$Method");
422         }
423         if (_delegate == null) {
424             _delegate = new NullDelegates.Method();
425         }
426     }
427 
428     private static Delegate attemptDelegate(String s) {
429         //String type = "method delegate";
430         try {
431             Class c = Class.forName(s);
432             return (Delegate)c.newInstance();
433         } catch (java.lang.ClassNotFoundException x) {
434             //System.err.println("Cannot find "+type+" "+s+": "+x);
435         } catch (java.lang.InstantiationException x) {
436             //System.err.println("Cannot instantiate "+type+" "+s+": "+x);
437         } catch (java.lang.IllegalAccessException x) {
438             //System.err.println("Cannot access "+type+" "+s+": "+x);
439         }
440         return null;
441     }
442 }