1
2
3
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
21
22
23 public abstract class jq_Method extends jq_Member {
24
25
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
39 protected jq_CompiledCode default_compiled_version;
40
41
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
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
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
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
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
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;
270
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
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
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
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
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
430 try {
431 Class c = Class.forName(s);
432 return (Delegate)c.newInstance();
433 } catch (java.lang.ClassNotFoundException x) {
434
435 } catch (java.lang.InstantiationException x) {
436
437 } catch (java.lang.IllegalAccessException x) {
438
439 }
440 return null;
441 }
442 }