1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
package org.apache.commons.javaflow.bytecode.transformation.bcel; |
18 | |
|
19 | |
import org.apache.bcel.Constants; |
20 | |
import org.apache.bcel.Repository; |
21 | |
import org.apache.bcel.classfile.Attribute; |
22 | |
import org.apache.bcel.classfile.ClassFormatException; |
23 | |
import org.apache.bcel.classfile.ClassParser; |
24 | |
import org.apache.bcel.classfile.ConstantCP; |
25 | |
import org.apache.bcel.classfile.ConstantNameAndType; |
26 | |
import org.apache.bcel.classfile.ConstantPool; |
27 | |
import org.apache.bcel.classfile.ConstantUtf8; |
28 | |
import org.apache.bcel.classfile.JavaClass; |
29 | |
import org.apache.bcel.classfile.Method; |
30 | |
import org.apache.bcel.generic.ACONST_NULL; |
31 | |
import org.apache.bcel.generic.ASTORE; |
32 | |
import org.apache.bcel.generic.BasicType; |
33 | |
import org.apache.bcel.generic.ClassGen; |
34 | |
import org.apache.bcel.generic.ConstantPoolGen; |
35 | |
import org.apache.bcel.generic.GOTO; |
36 | |
import org.apache.bcel.generic.IFEQ; |
37 | |
import org.apache.bcel.generic.IFNULL; |
38 | |
import org.apache.bcel.generic.INVOKESTATIC; |
39 | |
import org.apache.bcel.generic.Instruction; |
40 | |
import org.apache.bcel.generic.InstructionConstants; |
41 | |
import org.apache.bcel.generic.InstructionFactory; |
42 | |
import org.apache.bcel.generic.InstructionHandle; |
43 | |
import org.apache.bcel.generic.InstructionList; |
44 | |
import org.apache.bcel.generic.InstructionTargeter; |
45 | |
import org.apache.bcel.generic.InvokeInstruction; |
46 | |
import org.apache.bcel.generic.MethodGen; |
47 | |
import org.apache.bcel.generic.ObjectType; |
48 | |
import org.apache.bcel.generic.PUSH; |
49 | |
import org.apache.bcel.generic.RET; |
50 | |
import org.apache.bcel.generic.ReferenceType; |
51 | |
import org.apache.bcel.generic.TABLESWITCH; |
52 | |
import org.apache.bcel.generic.TargetLostException; |
53 | |
import org.apache.bcel.generic.Type; |
54 | |
import org.apache.bcel.verifier.exc.AssertionViolatedException; |
55 | |
import org.apache.commons.javaflow.bytecode.Continuable; |
56 | |
import org.apache.commons.javaflow.bytecode.StackRecorder; |
57 | |
import org.apache.commons.javaflow.bytecode.transformation.ResourceTransformer; |
58 | |
import org.apache.commons.javaflow.bytecode.transformation.bcel.analyser.ControlFlowGraph; |
59 | |
import org.apache.commons.javaflow.bytecode.transformation.bcel.analyser.ExceptionHandler; |
60 | |
import org.apache.commons.javaflow.bytecode.transformation.bcel.analyser.ExecutionPath; |
61 | |
import org.apache.commons.javaflow.bytecode.transformation.bcel.analyser.ExecutionVisitor; |
62 | |
import org.apache.commons.javaflow.bytecode.transformation.bcel.analyser.Frame; |
63 | |
import org.apache.commons.javaflow.bytecode.transformation.bcel.analyser.InstructionContext; |
64 | |
import org.apache.commons.javaflow.bytecode.transformation.bcel.analyser.LocalVariables; |
65 | |
import org.apache.commons.javaflow.bytecode.transformation.bcel.analyser.OperandStack; |
66 | |
import org.apache.commons.javaflow.bytecode.transformation.bcel.analyser.UninitializedObjectType; |
67 | |
import org.apache.commons.logging.Log; |
68 | |
import org.apache.commons.logging.LogFactory; |
69 | |
|
70 | |
import java.io.ByteArrayInputStream; |
71 | |
import java.io.FileOutputStream; |
72 | |
import java.io.IOException; |
73 | |
import java.io.InputStream; |
74 | |
import java.util.Vector; |
75 | |
|
76 | |
|
77 | |
|
78 | |
|
79 | |
|
80 | |
|
81 | |
|
82 | |
|
83 | |
public final class BcelClassTransformer implements ResourceTransformer { |
84 | |
|
85 | 0 | private final static Log log = LogFactory.getLog(BcelClassTransformer.class); |
86 | |
|
87 | 0 | private static final String STACK_RECORDER_CLASS = StackRecorder.class.getName(); |
88 | 0 | private static final ObjectType STACK_RECORDER_TYPE = new ObjectType(STACK_RECORDER_CLASS); |
89 | 0 | private static final String CONTINUABLE_CLASS = Continuable.class.getName(); |
90 | |
private static final String STACK_METHOD = "get"; |
91 | |
private static final String POP_METHOD = "pop"; |
92 | |
private static final String PUSH_METHOD = "push"; |
93 | |
private static final String RESTORING_FIELD = "isRestoring"; |
94 | |
private static final String CAPTURING_FIELD = "isCapturing"; |
95 | |
|
96 | 0 | private boolean currentMethodStatic = false; |
97 | 0 | public static boolean debug = false; |
98 | |
|
99 | |
|
100 | |
|
101 | |
|
102 | |
|
103 | |
|
104 | |
|
105 | |
|
106 | |
|
107 | |
|
108 | |
|
109 | |
private org.apache.bcel.util.Repository repository; |
110 | |
|
111 | |
|
112 | |
|
113 | |
|
114 | |
|
115 | 0 | private static final Object repositoryLock = new Object(); |
116 | |
|
117 | |
static { |
118 | |
try { |
119 | 0 | debug = System.getProperty(BcelClassTransformer.class.getName()+".debug")!=null; |
120 | 0 | } catch (SecurityException e) { |
121 | |
|
122 | 0 | } |
123 | 0 | } |
124 | |
|
125 | 0 | public BcelClassTransformer() { |
126 | 0 | } |
127 | |
|
128 | 0 | public BcelClassTransformer(org.apache.bcel.util.Repository repository) { |
129 | 0 | this.repository = repository; |
130 | 0 | } |
131 | |
|
132 | |
public byte[] transform(final byte[] original) { |
133 | 0 | if(repository==null) { |
134 | 0 | return doTransform(original); |
135 | |
} else { |
136 | 0 | synchronized(repositoryLock) { |
137 | 0 | org.apache.bcel.util.Repository old = Repository.getRepository(); |
138 | 0 | Repository.setRepository(repository); |
139 | |
try { |
140 | 0 | return doTransform(original); |
141 | |
} finally { |
142 | 0 | Repository.setRepository(old); |
143 | 0 | } |
144 | 0 | } |
145 | |
} |
146 | |
} |
147 | |
|
148 | |
private byte[] doTransform(final byte[] original) { |
149 | |
|
150 | 0 | final InputStream is = new ByteArrayInputStream(original); |
151 | |
|
152 | 0 | final ClassParser parser = new ClassParser(is, null); |
153 | 0 | JavaClass javaClazz = null; |
154 | |
try { |
155 | 0 | javaClazz = parser.parse(); |
156 | 0 | } catch (ClassFormatException e2) { |
157 | 0 | e2.printStackTrace(); |
158 | 0 | } catch (IOException e2) { |
159 | 0 | e2.printStackTrace(); |
160 | 0 | } |
161 | |
|
162 | 0 | Repository.addClass(javaClazz); |
163 | |
|
164 | 0 | log.debug("transforming class " + javaClazz.getClassName()); |
165 | |
|
166 | |
|
167 | |
|
168 | |
{ |
169 | 0 | String[] intfs = javaClazz.getInterfaceNames(); |
170 | 0 | for( int i=0; i<intfs.length; i++ ) |
171 | 0 | if(intfs[i].equals(CONTINUABLE_CLASS)) { |
172 | |
|
173 | 0 | log.debug(javaClazz.getClassName()+" is already instrumented. Skipping"); |
174 | 0 | return original; |
175 | |
} |
176 | |
} |
177 | |
|
178 | 0 | final ClassGen clazzGen = new ClassGen(javaClazz); |
179 | 0 | final ConstantPoolGen cp = clazzGen.getConstantPool(); |
180 | |
|
181 | 0 | if(debug) { |
182 | 0 | dump(javaClazz, ".orig"); |
183 | |
} |
184 | |
|
185 | |
|
186 | 0 | final ExecutionVisitor ev = new ExecutionVisitor(); |
187 | 0 | ev.setConstantPoolGen(cp); |
188 | |
|
189 | |
|
190 | |
|
191 | |
|
192 | 0 | final Method[] methods = clazzGen.getMethods(); |
193 | 0 | for (int i = 0; i < methods.length; i++) { |
194 | 0 | final MethodGen method = new MethodGen(methods[i], clazzGen.getClassName(), cp); |
195 | 0 | currentMethodStatic = methods[i].isStatic(); |
196 | 0 | if (needsRewriting(method)) { |
197 | |
|
198 | |
|
199 | 0 | final ControlFlowGraph cfg = new ControlFlowGraph(method); |
200 | |
|
201 | 0 | analyse(clazzGen, method, cfg, ev); |
202 | |
|
203 | |
try { |
204 | |
|
205 | 0 | rewrite(method, cfg); |
206 | 0 | } catch (ClassNotFoundException e1) { |
207 | 0 | e1.printStackTrace(); |
208 | 0 | } |
209 | |
|
210 | 0 | clazzGen.replaceMethod(methods[i], method.getMethod()); |
211 | |
} |
212 | |
} |
213 | |
|
214 | 0 | clazzGen.addInterface(CONTINUABLE_CLASS); |
215 | 0 | JavaClass newClass = clazzGen.getJavaClass(); |
216 | 0 | final byte[] changed = newClass.getBytes(); |
217 | |
|
218 | 0 | if(debug) { |
219 | 0 | dump(newClass, ".rewritten"); |
220 | |
} |
221 | |
|
222 | 0 | return changed; |
223 | |
} |
224 | |
|
225 | |
|
226 | |
|
227 | |
|
228 | |
private void dump(JavaClass javaClazz, String suffix) { |
229 | 0 | String path = javaClazz.getClassName()+suffix; |
230 | |
|
231 | 0 | final byte[] orig = javaClazz.getBytes(); |
232 | |
|
233 | 0 | FileOutputStream out = null; |
234 | |
try { |
235 | 0 | out = new FileOutputStream(path); |
236 | |
|
237 | 0 | log.debug("writing " + path); |
238 | |
|
239 | 0 | out.write(orig); |
240 | 0 | out.flush(); |
241 | 0 | } catch (final IOException e) { |
242 | 0 | e.printStackTrace(); |
243 | |
|
244 | |
try { |
245 | 0 | if (out != null) { |
246 | 0 | out.close(); |
247 | |
} |
248 | 0 | } catch (final IOException e1) { |
249 | 0 | log.error(e1.getMessage(), e1); |
250 | |
} finally { |
251 | 0 | out = null; |
252 | 0 | } |
253 | 0 | } |
254 | |
|
255 | |
try { |
256 | 0 | out = new FileOutputStream(path + ".java"); |
257 | |
|
258 | 0 | log.debug("writing " + path + ".java"); |
259 | |
|
260 | 0 | final DecompilingVisitor v = new DecompilingVisitor(javaClazz, out); |
261 | 0 | v.start(); |
262 | 0 | } catch (final Exception e) { |
263 | 0 | e.printStackTrace(); |
264 | |
|
265 | |
try { |
266 | 0 | if (out != null) { |
267 | 0 | out.close(); |
268 | |
} |
269 | 0 | } catch (final IOException e1) { |
270 | 0 | log.error(e1.getMessage(), e1); |
271 | |
} finally { |
272 | 0 | out = null; |
273 | 0 | } |
274 | 0 | } |
275 | 0 | } |
276 | |
|
277 | |
private boolean needsRewriting(MethodGen m) { |
278 | 0 | if (m.getName().equals(Constants.CONSTRUCTOR_NAME) |
279 | |
|| m.getName().equals(Constants.STATIC_INITIALIZER_NAME) |
280 | |
|| m.isNative() |
281 | |
|| m.isAbstract()) { |
282 | 0 | return false; |
283 | |
} else { |
284 | 0 | return true; |
285 | |
} |
286 | |
} |
287 | |
|
288 | |
private void analyse(ClassGen clazz, MethodGen method, ControlFlowGraph cfg, ExecutionVisitor ev) { |
289 | 0 | log.debug("analyse " + method.getName()); |
290 | |
|
291 | 0 | final Frame vanillaFrame = craeteInitialFrame(method, clazz); |
292 | |
|
293 | 0 | final Vector ics = new Vector(); |
294 | 0 | final Vector ecs = new Vector(); |
295 | |
|
296 | 0 | final InstructionContext start = cfg.contextOf(method.getInstructionList().getStart()); |
297 | |
|
298 | 0 | start.execute(vanillaFrame, ExecutionPath.EMPTY, ev); |
299 | |
|
300 | |
|
301 | 0 | ics.add(start); |
302 | 0 | ecs.add(ExecutionPath.EMPTY); |
303 | |
|
304 | 0 | while (!ics.isEmpty()) { |
305 | 0 | final InstructionContext u = (InstructionContext) ics.remove(0); |
306 | 0 | final ExecutionPath oldchain = (ExecutionPath) ecs.remove(0); |
307 | 0 | final ExecutionPath newchain = oldchain.append(u); |
308 | |
|
309 | 0 | if ((u.getInstruction().getInstruction()) instanceof RET) { |
310 | |
|
311 | 0 | InstructionHandle jsr = oldchain.lastExecutionJSR().getInstruction(); |
312 | |
|
313 | 0 | final InstructionContext theSuccessor = cfg.contextOf(jsr.getNext()); |
314 | |
|
315 | 0 | if (theSuccessor.execute(u.getOutFrame(oldchain), newchain, ev)) { |
316 | 0 | ics.add(theSuccessor); |
317 | 0 | ecs.add(newchain); |
318 | |
} |
319 | 0 | } else { |
320 | |
|
321 | 0 | final InstructionContext[] succs = u.getSuccessors(); |
322 | 0 | for (int s = 0; s < succs.length; s++) { |
323 | 0 | final InstructionContext v = succs[s]; |
324 | |
|
325 | 0 | if (v.execute(u.getOutFrame(oldchain), newchain, ev)) { |
326 | 0 | ics.add(v); |
327 | 0 | ecs.add(newchain); |
328 | |
} |
329 | |
} |
330 | |
} |
331 | |
|
332 | 0 | final ExceptionHandler[] exc_hds = u.getExceptionHandlers(); |
333 | 0 | for (int s = 0; s < exc_hds.length; s++) { |
334 | 0 | final InstructionContext v = cfg.contextOf(exc_hds[s].getHandlerStart()); |
335 | |
|
336 | |
|
337 | |
|
338 | |
|
339 | |
|
340 | |
|
341 | |
|
342 | |
|
343 | 0 | final LocalVariables newLocals = u.getOutFrame(oldchain).getLocals(); |
344 | 0 | final OperandStack newStack = new OperandStack( |
345 | |
u.getOutFrame(oldchain).getStack().maxStack(), |
346 | |
(exc_hds[s].getExceptionType() == null ? Type.THROWABLE : exc_hds[s] |
347 | |
.getExceptionType())); |
348 | 0 | final Frame newFrame = new Frame(newLocals, newStack); |
349 | |
|
350 | 0 | if (v.execute(newFrame, ExecutionPath.EMPTY, ev)) { |
351 | 0 | ics.add(v); |
352 | 0 | ecs.add(ExecutionPath.EMPTY); |
353 | |
} |
354 | |
} |
355 | 0 | } |
356 | 0 | } |
357 | |
|
358 | |
|
359 | |
|
360 | |
|
361 | |
|
362 | |
private Frame craeteInitialFrame(MethodGen method, ClassGen clazz) { |
363 | 0 | final Frame vanillaFrame = new Frame(method.getMaxLocals(), method.getMaxStack()); |
364 | 0 | if (!method.isStatic()) { |
365 | 0 | if (method.getName().equals(Constants.CONSTRUCTOR_NAME)) { |
366 | 0 | Frame._this = new UninitializedObjectType(new ObjectType(clazz.getClassName())); |
367 | 0 | vanillaFrame.getLocals().set(0, new UninitializedObjectType(new ObjectType(clazz.getClassName()))); |
368 | 0 | } else { |
369 | 0 | Frame._this = null; |
370 | 0 | vanillaFrame.getLocals().set(0, new ObjectType(clazz.getClassName())); |
371 | |
} |
372 | |
} |
373 | |
|
374 | 0 | final Type[] argtypes = method.getArgumentTypes(); |
375 | 0 | int twoslotoffset = 0; |
376 | 0 | for (int j = 0; j < argtypes.length; j++) { |
377 | 0 | if ((argtypes[j] == Type.SHORT) || (argtypes[j] == Type.BYTE) || (argtypes[j] == Type.CHAR) || (argtypes[j] == Type.BOOLEAN)) { |
378 | 0 | argtypes[j] = Type.INT; |
379 | |
} |
380 | 0 | vanillaFrame.getLocals().set(twoslotoffset + j + (method.isStatic() ? 0 : 1), argtypes[j]); |
381 | 0 | if (argtypes[j].getSize() == 2) { |
382 | 0 | twoslotoffset++; |
383 | 0 | vanillaFrame.getLocals().set(twoslotoffset + j + (method.isStatic() ? 0 : 1), Type.UNKNOWN); |
384 | |
} |
385 | |
} |
386 | |
|
387 | 0 | return vanillaFrame; |
388 | |
} |
389 | |
|
390 | |
private void rewrite(MethodGen method, ControlFlowGraph cfg) throws ClassNotFoundException { |
391 | 0 | final InstructionFactory insFactory = new InstructionFactory(method.getConstantPool()); |
392 | 0 | final Vector invokeIns = new Vector(); |
393 | 0 | final InstructionList insList = method.getInstructionList(); |
394 | 0 | InstructionHandle ins = insList.getStart(); |
395 | 0 | final InstructionList restorer = new InstructionList(); |
396 | 0 | int count = 0; |
397 | |
|
398 | |
|
399 | |
|
400 | 0 | int[] localVarsSize = new int[1]; |
401 | 0 | localVarsSize[0] = method.getMaxLocals(); |
402 | |
|
403 | 0 | while (ins != null) { |
404 | 0 | InstructionHandle next = ins.getNext(); |
405 | |
|
406 | |
|
407 | 0 | InstructionContext context = null; |
408 | 0 | Frame frame = null; |
409 | |
try { |
410 | 0 | context = cfg.contextOf(ins); |
411 | 0 | frame = context.getOutFrame(ExecutionPath.EMPTY); |
412 | 0 | } catch (AssertionViolatedException ave) { |
413 | |
|
414 | 0 | } |
415 | 0 | if (frame != null) { |
416 | 0 | if (rewriteable(method, ins)) { |
417 | |
|
418 | |
|
419 | |
|
420 | 0 | final InvokeInstruction invoke = (InvokeInstruction) ins.getInstruction(); |
421 | 0 | final Type[] arguments = invoke.getArgumentTypes(method.getConstantPool()); |
422 | 0 | ReferenceType objecttype = null; |
423 | 0 | if (!(invoke instanceof INVOKESTATIC)) { |
424 | 0 | objecttype = (ReferenceType) context.getInFrame().getStack().peek(arguments.length); |
425 | |
} |
426 | 0 | final InstructionList rList = restoreFrame(method, ins, insFactory, frame, objecttype); |
427 | 0 | insList.append(ins, saveFrame(method, ins, count++, insFactory, frame)); |
428 | 0 | invokeIns.addElement(rList.getStart()); |
429 | 0 | restorer.append(rList); |
430 | |
} |
431 | |
|
432 | 0 | if (ins.getInstruction().getOpcode() == Constants.NEW) { |
433 | |
try { |
434 | |
|
435 | 0 | while (next != null && next.getInstruction().getOpcode() == Constants.DUP) { |
436 | 0 | context = cfg.contextOf(next); |
437 | 0 | context.getOutFrame(ExecutionPath.EMPTY); |
438 | 0 | final InstructionHandle newnext = next.getNext(); |
439 | 0 | insList.delete(next); |
440 | 0 | next = newnext; |
441 | 0 | } |
442 | |
|
443 | |
|
444 | |
|
445 | |
|
446 | |
|
447 | |
|
448 | |
|
449 | |
|
450 | |
|
451 | |
|
452 | |
|
453 | |
|
454 | |
|
455 | |
|
456 | |
|
457 | |
|
458 | |
|
459 | 0 | if(next!=null && next.getNext()!=null && next.getNext().getInstruction().getOpcode() == Constants.DUP_X2) { |
460 | 0 | InstructionHandle dupx2ptr = next.getNext(); |
461 | 0 | final InstructionHandle newnext = dupx2ptr.getNext(); |
462 | 0 | insList.insert(dupx2ptr, InstructionConstants.DUP); |
463 | 0 | insList.delete(dupx2ptr); |
464 | 0 | next = newnext; |
465 | |
} |
466 | 0 | final InstructionTargeter[] targeter = ins.getTargeters(); |
467 | 0 | if (targeter != null) { |
468 | 0 | final InstructionHandle newnext = ins.getNext(); |
469 | 0 | for (int i = 0; i < targeter.length; i++) { |
470 | 0 | targeter[i].updateTarget(ins, newnext); |
471 | |
} |
472 | |
} |
473 | 0 | insList.delete(ins); |
474 | 0 | } catch (TargetLostException tle) { |
475 | 0 | throw new ClassNotFoundException(tle.getMessage(), tle); |
476 | 0 | } |
477 | 0 | } else if (ins.getInstruction().getOpcode() == Constants.INVOKESPECIAL) { |
478 | |
|
479 | |
|
480 | 0 | frame = context.getInFrame(); |
481 | 0 | final InvokeInstruction invoke = (InvokeInstruction) ins.getInstruction(); |
482 | 0 | final Type[] arguments = invoke.getArgumentTypes(method.getConstantPool()); |
483 | |
|
484 | 0 | final OperandStack os = frame.getStack(); |
485 | 0 | final Type type = os.peek(arguments.length); |
486 | 0 | if (type instanceof UninitializedObjectType) { |
487 | 0 | final ObjectType objecttype = ((UninitializedObjectType) type).getInitialized(); |
488 | 0 | final InstructionList duplicator = duplicateStack(method, invoke, objecttype, localVarsSize); |
489 | 0 | final InstructionTargeter[] targeter = ins.getTargeters(); |
490 | |
|
491 | 0 | if (targeter != null) { |
492 | 0 | final InstructionHandle newnext = duplicator.getStart(); |
493 | 0 | for (int i = 0; i < targeter.length; i++) { |
494 | 0 | targeter[i].updateTarget(ins, newnext); |
495 | |
} |
496 | |
} |
497 | 0 | insList.insert(ins, duplicator); |
498 | |
} |
499 | |
} |
500 | |
} |
501 | 0 | ins = next; |
502 | 0 | } |
503 | |
|
504 | |
|
505 | 0 | final int varStack = method.getMaxLocals(); |
506 | |
|
507 | 0 | Instruction loadStackRecorder = InstructionFactory.createLoad(STACK_RECORDER_TYPE, varStack); |
508 | |
|
509 | 0 | final InstructionHandle firstIns = insList.getStart(); |
510 | 0 | if (count > 0) { |
511 | 0 | final InstructionHandle[] tableTargets = new InstructionHandle[count]; |
512 | 0 | int[] match = new int[count]; |
513 | 0 | for (int i = 0; i < count; i++) { |
514 | 0 | match[i] = i; |
515 | |
} |
516 | 0 | invokeIns.copyInto(tableTargets); |
517 | 0 | insList.insert(restorer); |
518 | |
|
519 | |
|
520 | 0 | insList.insert(new TABLESWITCH(match, tableTargets, firstIns)); |
521 | 0 | insList.insert(insFactory.createInvoke(STACK_RECORDER_CLASS, getPopMethod(Type.INT), Type.INT, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
522 | 0 | insList.insert(loadStackRecorder); |
523 | |
|
524 | |
|
525 | 0 | insList.insert(new IFEQ(firstIns)); |
526 | 0 | insList.insert(insFactory.createFieldAccess(STACK_RECORDER_CLASS, RESTORING_FIELD, Type.BOOLEAN, Constants.GETFIELD)); |
527 | 0 | insList.insert(loadStackRecorder); |
528 | |
|
529 | |
|
530 | 0 | insList.insert(new IFNULL(firstIns)); |
531 | |
|
532 | |
|
533 | 0 | insList.insert(InstructionFactory.createStore(STACK_RECORDER_TYPE, varStack)); |
534 | 0 | insList.insert(InstructionConstants.DUP); |
535 | 0 | insList.insert(insFactory.createInvoke(STACK_RECORDER_CLASS, STACK_METHOD, STACK_RECORDER_TYPE, Type.NO_ARGS, Constants.INVOKESTATIC)); |
536 | |
|
537 | |
|
538 | 0 | localVarsSize[0] = Math.max( localVarsSize[0], method.getMaxLocals()+1 ); |
539 | |
|
540 | |
|
541 | 0 | method.setMaxStack(method.getMaxStack() + 2); |
542 | |
} |
543 | |
|
544 | 0 | method.setMaxLocals(localVarsSize[0]); |
545 | |
|
546 | |
|
547 | |
|
548 | |
|
549 | 0 | method.setMaxStack(method.getMaxStack() + 1); |
550 | |
|
551 | |
|
552 | |
|
553 | |
|
554 | 0 | Attribute[] atts = method.getCodeAttributes(); |
555 | 0 | for( int i=0; i<atts.length; i++ ) { |
556 | 0 | if(atts[i].getNameIndex()==method.getConstantPool().lookupUtf8("LocalVariableTypeTable")) |
557 | 0 | method.removeCodeAttribute(atts[i]); |
558 | |
} |
559 | 0 | } |
560 | |
|
561 | |
|
562 | |
|
563 | |
|
564 | |
private InstructionList duplicateStack(MethodGen method, InvokeInstruction invoke, ObjectType objecttype, int[] localVarsSize) { |
565 | |
|
566 | 0 | final InstructionFactory insFactory = new InstructionFactory(method.getConstantPool()); |
567 | 0 | final InstructionList insList = new InstructionList(); |
568 | |
|
569 | 0 | final Type[] arguments = invoke.getArgumentTypes(method.getConstantPool()); |
570 | |
|
571 | 0 | int localVarOffset = method.getMaxLocals()+1; |
572 | |
|
573 | |
|
574 | 0 | for (int i = arguments.length - 1; i >= 0; i--) { |
575 | 0 | Type type = arguments[i]; |
576 | 0 | insList.append(InstructionFactory.createStore(type,localVarOffset)); |
577 | 0 | localVarOffset += type.getSize(); |
578 | |
} |
579 | |
|
580 | 0 | localVarsSize[0] = Math.max(localVarsSize[0],localVarOffset); |
581 | |
|
582 | |
|
583 | 0 | insList.append(insFactory.createNew(objecttype)); |
584 | 0 | insList.append(InstructionConstants.DUP); |
585 | |
|
586 | |
|
587 | 0 | for (int i = 0; i < arguments.length; i++) { |
588 | 0 | Type type = arguments[i]; |
589 | |
|
590 | 0 | localVarOffset -= type.getSize(); |
591 | 0 | insList.append(InstructionFactory.createLoad(type,localVarOffset)); |
592 | 0 | if(type instanceof ReferenceType) { |
593 | |
|
594 | 0 | insList.append(InstructionConstants.ACONST_NULL); |
595 | 0 | insList.append(InstructionFactory.createStore(type,localVarOffset)); |
596 | |
} |
597 | |
} |
598 | 0 | return insList; |
599 | |
} |
600 | |
|
601 | |
private boolean rewriteable(MethodGen method, InstructionHandle handle) { |
602 | |
|
603 | 0 | int opcode = handle.getInstruction().getOpcode(); |
604 | |
|
605 | 0 | if(!(handle.getInstruction() instanceof InvokeInstruction)) |
606 | 0 | return false; |
607 | |
|
608 | 0 | if (opcode == Constants.INVOKESPECIAL) { |
609 | 0 | final InvokeInstruction ivs = (InvokeInstruction) handle.getInstruction(); |
610 | 0 | final String mName = ivs.getMethodName(method.getConstantPool()); |
611 | 0 | if(mName.equals(Constants.CONSTRUCTOR_NAME)) |
612 | 0 | return false; |
613 | |
} |
614 | |
|
615 | |
|
616 | |
|
617 | |
|
618 | |
|
619 | |
|
620 | |
|
621 | |
|
622 | |
|
623 | 0 | return true; |
624 | |
} |
625 | |
|
626 | |
private InstructionList saveFrame(MethodGen method, InstructionHandle handle, int pc, InstructionFactory insFactory, Frame frame) { |
627 | 0 | final InstructionList insList = new InstructionList(); |
628 | |
|
629 | |
|
630 | 0 | final InvokeInstruction inv = (InvokeInstruction) handle.getInstruction(); |
631 | 0 | final Type returnType = getReturnType(method.getConstantPool().getConstantPool(), inv.getIndex()); |
632 | 0 | if (returnType.getSize() > 0) { |
633 | 0 | insList.insert(InstructionFactory.createPop(returnType.getSize())); |
634 | |
} |
635 | 0 | boolean skipFirst = returnType.getSize() > 0; |
636 | |
|
637 | |
|
638 | |
|
639 | |
|
640 | |
|
641 | |
|
642 | 0 | Instruction loadStackRecorder = InstructionFactory.createLoad(STACK_RECORDER_TYPE, method.getMaxLocals()); |
643 | |
|
644 | |
|
645 | 0 | final OperandStack os = frame.getStack(); |
646 | 0 | for (int i = skipFirst ? 1 : 0; i < os.size(); i++) { |
647 | 0 | Type type = os.peek(i); |
648 | 0 | if (type instanceof BasicType) { |
649 | 0 | if (type.getSize() < 2 && !type.equals(Type.FLOAT)) { |
650 | 0 | type = Type.INT; |
651 | |
} |
652 | |
|
653 | |
|
654 | 0 | if (type.equals(Type.LONG) || type.equals(Type.DOUBLE)) { |
655 | 0 | insList.append(InstructionConstants.ACONST_NULL); |
656 | 0 | insList.append(loadStackRecorder); |
657 | 0 | insList.append(InstructionConstants.DUP2_X2); |
658 | 0 | insList.append(InstructionConstants.POP2); |
659 | 0 | } else { |
660 | 0 | insList.append(loadStackRecorder); |
661 | 0 | insList.append(InstructionConstants.SWAP); |
662 | |
} |
663 | 0 | insList.append(insFactory.createInvoke(STACK_RECORDER_CLASS, getPushMethod(type), Type.VOID, new Type[] { type }, Constants.INVOKEVIRTUAL)); |
664 | 0 | if (type.equals(Type.LONG) || type.equals(Type.DOUBLE)) |
665 | 0 | insList.append(InstructionConstants.POP); |
666 | 0 | } else if (type == null) { |
667 | 0 | insList.append(InstructionConstants.POP); |
668 | 0 | } else if (type instanceof UninitializedObjectType) { |
669 | |
|
670 | |
|
671 | 0 | } else if (type instanceof ReferenceType) { |
672 | 0 | if(type.equals(Type.NULL)) { |
673 | |
|
674 | |
|
675 | |
|
676 | 0 | insList.append(InstructionConstants.POP); |
677 | 0 | } else { |
678 | 0 | insList.append(loadStackRecorder); |
679 | 0 | insList.append(InstructionConstants.SWAP); |
680 | 0 | insList.append(insFactory.createInvoke(STACK_RECORDER_CLASS, getPushMethod(Type.OBJECT), Type.VOID, new Type[] { Type.OBJECT }, Constants.INVOKEVIRTUAL)); |
681 | |
} |
682 | |
} |
683 | |
} |
684 | |
|
685 | 0 | if (debug) { |
686 | 0 | insList.insert(insFactory.createPrintln("capturing invocation "+method)); |
687 | |
} |
688 | 0 | insList.insert(new IFEQ(handle.getNext())); |
689 | |
|
690 | |
|
691 | 0 | insList.insert(insFactory.createFieldAccess(STACK_RECORDER_CLASS, CAPTURING_FIELD, Type.BOOLEAN, Constants.GETFIELD)); |
692 | 0 | insList.insert(loadStackRecorder); |
693 | |
|
694 | |
|
695 | 0 | insList.insert(new IFNULL(handle.getNext())); |
696 | 0 | insList.insert(loadStackRecorder); |
697 | |
|
698 | |
|
699 | |
|
700 | |
|
701 | |
|
702 | |
|
703 | 0 | final LocalVariables lvs = frame.getLocals(); |
704 | 0 | for (int i = 0; i < lvs.maxLocals(); i++) { |
705 | 0 | Type type = lvs.get(i); |
706 | 0 | if (type instanceof BasicType) { |
707 | 0 | insList.append(loadStackRecorder); |
708 | 0 | insList.append(InstructionFactory.createLoad(type, i)); |
709 | 0 | if (type.getSize() < 2 && !type.equals(Type.FLOAT)) |
710 | 0 | type = Type.INT; |
711 | 0 | insList.append(insFactory.createInvoke(STACK_RECORDER_CLASS, getPushMethod(type), Type.VOID, new Type[] { type }, Constants.INVOKEVIRTUAL)); |
712 | 0 | } else if (type == null || type==Type.NULL) { |
713 | |
|
714 | 0 | } else if (type instanceof UninitializedObjectType) { |
715 | |
|
716 | 0 | } else if (type instanceof ReferenceType) { |
717 | 0 | if (i == 0 && !currentMethodStatic) { |
718 | |
|
719 | 0 | insList.append(loadStackRecorder); |
720 | 0 | insList.append(InstructionFactory.createLoad(type, i)); |
721 | 0 | insList.append(insFactory.createInvoke(STACK_RECORDER_CLASS, PUSH_METHOD + "Reference", Type.VOID, new Type[] { Type.OBJECT }, Constants.INVOKEVIRTUAL)); |
722 | |
} |
723 | 0 | insList.append(loadStackRecorder); |
724 | 0 | insList.append(InstructionFactory.createLoad(type, i)); |
725 | 0 | insList.append(insFactory.createInvoke(STACK_RECORDER_CLASS, getPushMethod(Type.OBJECT), Type.VOID, new Type[] { Type.OBJECT }, Constants.INVOKEVIRTUAL)); |
726 | |
} |
727 | |
} |
728 | |
|
729 | 0 | insList.append(loadStackRecorder); |
730 | 0 | insList.append(new PUSH(method.getConstantPool(), pc)); |
731 | 0 | insList.append(insFactory.createInvoke(STACK_RECORDER_CLASS, getPushMethod(Type.INT), Type.VOID, new Type[] { Type.INT }, Constants.INVOKEVIRTUAL)); |
732 | |
|
733 | 0 | insList.append(InstructionFactory.createNull(method.getReturnType())); |
734 | 0 | insList.append(InstructionFactory.createReturn(method.getReturnType())); |
735 | 0 | return insList; |
736 | |
} |
737 | |
|
738 | |
private InstructionList restoreFrame(MethodGen method, InstructionHandle handle, InstructionFactory insFactory, Frame frame, ReferenceType objecttype) { |
739 | 0 | final InstructionList insList = new InstructionList(); |
740 | |
|
741 | |
|
742 | |
|
743 | |
|
744 | |
|
745 | 0 | final LocalVariables lvs = frame.getLocals(); |
746 | 0 | Instruction loadStackRecorder = InstructionFactory.createLoad(STACK_RECORDER_TYPE, method.getMaxLocals()); |
747 | |
|
748 | 0 | for (int i = lvs.maxLocals() - 1; i >= 0; i--) { |
749 | 0 | Type type = lvs.get(i); |
750 | 0 | if (type instanceof BasicType) { |
751 | 0 | insList.append(loadStackRecorder); |
752 | 0 | if (type.getSize() < 2 && !type.equals(Type.FLOAT)) { |
753 | 0 | type = Type.INT; |
754 | |
} |
755 | 0 | insList.append(insFactory.createInvoke(STACK_RECORDER_CLASS, getPopMethod(type), type, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
756 | 0 | insList.append(InstructionFactory.createStore(type, i)); |
757 | 0 | } else if (type == null) { |
758 | 0 | insList.append(InstructionConstants.ACONST_NULL); |
759 | 0 | insList.append(new ASTORE(i)); |
760 | 0 | } else if (type instanceof UninitializedObjectType) { |
761 | |
|
762 | |
|
763 | 0 | throw new Error("assertion failure"); |
764 | 0 | } else if (type instanceof ReferenceType) { |
765 | 0 | if (type==Type.NULL) { |
766 | |
|
767 | |
|
768 | |
|
769 | 0 | insList.append(InstructionConstants.ACONST_NULL); |
770 | 0 | } else { |
771 | 0 | insList.append(loadStackRecorder); |
772 | 0 | insList.append(insFactory.createInvoke(STACK_RECORDER_CLASS, getPopMethod(Type.OBJECT), Type.OBJECT, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
773 | 0 | if (!type.equals(Type.OBJECT)) { |
774 | 0 | insList.append(insFactory.createCast(Type.OBJECT, type)); |
775 | |
} |
776 | |
} |
777 | 0 | insList.append(new ASTORE(i)); |
778 | |
} |
779 | |
} |
780 | |
|
781 | 0 | final InvokeInstruction inv = (InvokeInstruction) handle.getInstruction(); |
782 | 0 | final Type returnType = getReturnType(method.getConstantPool().getConstantPool(), inv.getIndex()); |
783 | 0 | boolean skipFirst = returnType.getSize() > 0; |
784 | |
|
785 | |
|
786 | |
|
787 | |
|
788 | |
|
789 | |
|
790 | 0 | final OperandStack os = frame.getStack(); |
791 | 0 | for (int i = os.size() - 1; i >= (skipFirst ? 1 : 0); i--) { |
792 | 0 | Type type = os.peek(i); |
793 | 0 | if (type instanceof BasicType) { |
794 | 0 | if (type.getSize() < 2 && !type.equals(Type.FLOAT)) { |
795 | 0 | type = Type.INT; |
796 | |
} |
797 | 0 | insList.append(loadStackRecorder); |
798 | 0 | insList.append(insFactory.createInvoke(STACK_RECORDER_CLASS, getPopMethod(type), type, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
799 | 0 | } else if (type == null || type==Type.NULL) { |
800 | 0 | insList.append(new ACONST_NULL()); |
801 | 0 | } else if (type instanceof UninitializedObjectType) { |
802 | |
|
803 | |
|
804 | 0 | } else if (type instanceof ReferenceType) { |
805 | 0 | insList.append(loadStackRecorder); |
806 | 0 | insList.append(insFactory.createInvoke(STACK_RECORDER_CLASS, getPopMethod(Type.OBJECT), Type.OBJECT, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
807 | 0 | if (!type.equals(Type.OBJECT)) |
808 | 0 | insList.append(insFactory.createCast(Type.OBJECT, type)); |
809 | |
} |
810 | |
} |
811 | |
|
812 | 0 | if (!(inv instanceof INVOKESTATIC)) { |
813 | 0 | insList.append(loadStackRecorder); |
814 | 0 | insList.append(insFactory.createInvoke(STACK_RECORDER_CLASS, POP_METHOD + "Reference", Type.OBJECT, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
815 | 0 | insList.append(insFactory.createCast(Type.OBJECT, objecttype)); |
816 | |
} |
817 | |
|
818 | 0 | final Type[] paramTypes = getParamTypes(method.getConstantPool().getConstantPool(), inv.getIndex()); |
819 | 0 | for (int j = 0; j < paramTypes.length; j++) { |
820 | 0 | insList.append(InstructionFactory.createNull(paramTypes[j])); |
821 | |
} |
822 | |
|
823 | 0 | insList.append(new GOTO(handle)); |
824 | 0 | return insList; |
825 | |
} |
826 | |
|
827 | |
private Type[] getParamTypes(ConstantPool cp, int index) { |
828 | 0 | final ConstantCP cmr = (ConstantCP) cp.getConstant(index); |
829 | 0 | final ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(cmr.getNameAndTypeIndex()); |
830 | 0 | final String sig = ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes(); |
831 | 0 | return Type.getArgumentTypes(sig); |
832 | |
} |
833 | |
|
834 | |
private Type getReturnType(ConstantPool cp, int index) { |
835 | 0 | final ConstantCP cmr = (ConstantCP) cp.getConstant(index); |
836 | 0 | final ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(cmr.getNameAndTypeIndex()); |
837 | 0 | final String sig = ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes(); |
838 | 0 | return Type.getReturnType(sig); |
839 | |
} |
840 | |
|
841 | |
private String getPopMethod(Type type) { |
842 | 0 | return POP_METHOD + getTypeSuffix(type); |
843 | |
} |
844 | |
|
845 | |
private String getPushMethod(Type type) { |
846 | 0 | return PUSH_METHOD + getTypeSuffix(type); |
847 | |
} |
848 | |
|
849 | |
private String getTypeSuffix(Type type) { |
850 | 0 | if (type.equals(Type.BOOLEAN)) |
851 | 0 | return "Int"; |
852 | 0 | else if (type.equals(Type.CHAR)) |
853 | 0 | return "Int"; |
854 | 0 | else if (type.equals(Type.FLOAT)) |
855 | 0 | return "Float"; |
856 | 0 | else if (type.equals(Type.DOUBLE)) |
857 | 0 | return "Double"; |
858 | 0 | else if (type.equals(Type.BYTE)) |
859 | 0 | return "Int"; |
860 | 0 | else if (type.equals(Type.SHORT)) |
861 | 0 | return "Int"; |
862 | 0 | else if (type.equals(Type.INT)) |
863 | 0 | return "Int"; |
864 | 0 | else if (type.equals(Type.LONG)) |
865 | 0 | return "Long"; |
866 | |
|
867 | 0 | return "Object"; |
868 | |
} |
869 | |
|
870 | |
} |