KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > bytecode > CodeEnhancer


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.bytecode;
31
32 import com.caucho.log.Log;
33 import com.caucho.util.ByteBuffer;
34 import com.caucho.util.IntArray;
35 import com.caucho.util.L10N;
36
37 import java.util.ArrayList JavaDoc;
38 import java.util.logging.Level JavaDoc;
39 import java.util.logging.Logger JavaDoc;
40
41 /**
42  * Visitor for travelling the code.
43  */

44 public class CodeEnhancer extends CodeVisitor {
45   static private final Logger JavaDoc log = Log.open(CodeEnhancer.class);
46   static private final L10N L = new L10N(CodeEnhancer.class);
47
48   private ByteBuffer _code;
49
50   private ArrayList JavaDoc<Jump> _jumps;
51   private ArrayList JavaDoc<Switch> _switches;
52   private boolean _changeLength;
53
54   // already visited targets
55
private IntArray _pendingTargets;
56   private IntArray _completedTargets;
57
58   public CodeEnhancer()
59   {
60   }
61
62   public CodeEnhancer(JavaClass javaClass, CodeAttribute code)
63   {
64     init(javaClass, code);
65   }
66
67   public void init(JavaClass javaClass, CodeAttribute codeAttr)
68   {
69     super.init(javaClass, codeAttr);
70
71     _code = new ByteBuffer();
72     
73     byte []codeBuffer = codeAttr.getCode();
74     
75     _code.add(codeBuffer, 0, codeBuffer.length);
76
77     _changeLength = false;
78   }
79
80   /**
81    * Analyzes the code for a method
82    */

83   public void analyze(Analyzer analyzer, boolean allowFlow)
84     throws Exception JavaDoc
85   {
86     _pendingTargets = new IntArray();
87     _completedTargets = new IntArray();
88     
89     analyzeImpl(analyzer, allowFlow, _pendingTargets, _completedTargets);
90   }
91
92   /**
93    * Returns the code buffer.
94    */

95   public byte []getCode()
96   {
97     return _code.getBuffer();
98   }
99
100   /**
101    * Returns the length.
102    */

103   public int getLength()
104   {
105     return _code.getLength();
106   }
107
108   /**
109    * Adds a byte to the code.
110    */

111   public void addByte(int offset, int value)
112   {
113     insertCode(offset, 1);
114     
115     _code.set(offset, value);
116   }
117
118   /**
119    * Adds a byte to the code.
120    */

121   public void setByte(int offset, int value)
122   {
123     _code.set(offset, value);
124   }
125
126   /**
127    * Adds a short to the code.
128    */

129   public void addShort(int offset, int value)
130   {
131     insertCode(offset, 2);
132     
133     _code.set(offset + 0, value >> 8);
134     _code.set(offset + 1, value);
135   }
136
137   /**
138    * Adds a byte to the code.
139    */

140   public void add(int offset, byte []buffer, int bufOffset, int length)
141   {
142     insertCode(offset, length);
143     
144     _code.set(offset, buffer, bufOffset, length);
145   }
146
147   /**
148    * Removes a range from the code.
149    */

150   public void remove(int offset, int count)
151   {
152     removeCode(offset, count);
153   }
154
155   /**
156    * Adds a byte to the code.
157    */

158   public void addNulls(int offset, int count)
159   {
160     insertCode(offset, count);
161   }
162
163   /**
164    * Updates indices when adding a chunk of code. The operation at
165    * the given offset moves, e.g. adding 6 bytes to the beginning of
166    * the program moves the initial byte down by 6 and therefore needs
167    * to update the links as well.
168    *
169    * Therefore, enhancers which expand an opcode from 2 bytes to 3 bytes
170    * must insert the new bytes after the initial opcode.
171    */

172   protected void insertCode(int offset, int count)
173   {
174     if (_jumps == null)
175       analyzeJumps();
176
177     // XXX: revisits the new code
178
if (offset <= _offset) {
179       _offset += count;
180     }
181
182     for (int i = 0; i < _jumps.size(); i++) {
183       Jump jump = _jumps.get(i);
184
185       jump.insert(this, offset, count);
186     }
187     
188     ArrayList JavaDoc<CodeAttribute.ExceptionItem> exns = getExceptions();
189
190     for (int i = 0; i < exns.size(); i++) {
191       CodeAttribute.ExceptionItem exn = exns.get(i);
192
193       if (offset <= exn.getStart())
194     exn.setStart(exn.getStart() + count);
195       
196       if (offset <= exn.getEnd())
197     exn.setEnd(exn.getEnd() + count);
198       
199       if (offset <= exn.getHandler())
200     exn.setHandler(exn.getHandler() + count);
201     }
202
203     if (_pendingTargets != null) {
204       for (int i = _pendingTargets.size() - 1; i >= 0; i--) {
205     int target = _pendingTargets.get(i);
206
207     if (offset <= target)
208       _pendingTargets.set(i, target + count);
209       }
210       
211       for (int i = _completedTargets.size() - 1; i >= 0; i--) {
212     int target = _completedTargets.get(i);
213
214     if (offset <= target)
215       _completedTargets.set(i, target + count);
216       }
217     }
218
219     for (int i = 0; i < _switches.size(); i++) {
220       Branch branch = _switches.get(i);
221
222       branch.insert(this, offset, count);
223     }
224
225     for (int i = 0; i < count; i++)
226       _code.add(offset, 0);
227
228     for (int i = 0; i < _switches.size(); i++) {
229       Switch branch = _switches.get(i);
230
231       branch.insertPad(this, offset, count);
232     }
233   }
234   
235   protected void removeCode(int offset, int count)
236   {
237     if (_jumps == null)
238       analyzeJumps();
239
240     if (offset + count < _offset)
241       _offset -= count;
242     else if (offset <= _offset)
243       _offset = offset;
244
245     for (int i = 0; i < _jumps.size(); i++) {
246       Branch jump = _jumps.get(i);
247
248       jump.remove(this, offset, count);
249     }
250     
251     ArrayList JavaDoc<CodeAttribute.ExceptionItem> exns = getExceptions();
252
253     for (int i = 0; i < exns.size(); i++) {
254       CodeAttribute.ExceptionItem exn = exns.get(i);
255
256       exn.setStart(remove(exn.getStart(), offset, count));
257       exn.setEnd(remove(exn.getEnd(), offset, count));
258       exn.setHandler(remove(exn.getHandler(), offset, count));
259     }
260
261     if (_pendingTargets != null) {
262       for (int i = _pendingTargets.size() - 1; i >= 0; i--) {
263     int target = _pendingTargets.get(i);
264
265     _pendingTargets.set(i, remove(target, offset, count));
266       }
267       
268       for (int i = _completedTargets.size() - 1; i >= 0; i--) {
269     int target = _completedTargets.get(i);
270
271     _completedTargets.set(i, remove(target, offset, count));
272       }
273     }
274
275     for (int i = 0; i < _switches.size(); i++) {
276       Branch branch = _switches.get(i);
277
278       branch.remove(this, offset, count);
279     }
280
281     _code.remove(offset, count);
282
283     for (int i = 0; i < _switches.size(); i++) {
284       Switch branch = _switches.get(i);
285
286       branch.removePad(this, offset, count);
287     }
288   }
289
290   protected void analyzeJumps()
291   {
292     _jumps = new ArrayList JavaDoc<Jump>();
293     _switches = new ArrayList JavaDoc<Switch>();
294
295     _changeLength = true;
296
297     JumpAnalyzer analyzer = new JumpAnalyzer();
298
299     CodeVisitor visitor = new CodeVisitor(getJavaClass(), getCodeAttribute());
300
301     try {
302       visitor.analyze(analyzer);
303     } catch (Exception JavaDoc e) {
304       log.log(Level.WARNING, e.toString(), e);
305     }
306   }
307
308   /**
309    * Updates the code.
310    */

311   public void update()
312   {
313     byte []code = new byte[_code.size()];
314
315     System.arraycopy(_code.getBuffer(), 0, code, 0, _code.size());
316
317     _codeAttr.setCode(code);
318
319     if (_changeLength) {
320       // XXX: really need more sophisticated solution
321
ArrayList JavaDoc<Attribute> attrList = getCodeAttribute().getAttributes();
322       for (int i = attrList.size() - 1; i >= 0; i--) {
323     Attribute attr = attrList.get(i);
324
325     if (attr.getName().equals("LineNumberTable"))
326       attrList.remove(i);
327       }
328     }
329   }
330
331   private int remove(int pc, int offset, int count)
332   {
333     if (pc < offset)
334       return pc;
335     else if (pc < offset + count)
336       return offset;
337     else
338       return pc - count;
339   }
340
341   abstract static class Branch {
342     abstract void insert(CodeEnhancer enhancer, int offset, int count);
343     abstract void remove(CodeEnhancer enhancer, int offset, int count);
344   }
345     
346   static class Jump extends Branch {
347     private int _src;
348     private int _delta;
349
350     Jump(int src, int delta)
351     {
352       _src = src;
353       _delta = delta;
354     }
355
356     void insert(CodeEnhancer enhancer, int offset, int count)
357     {
358       // offset is before the jump
359
if (offset <= _src && offset <= _src + _delta) {
360     _src += count;
361       }
362       // offset is inside a forward jump
363
else if (_src < offset && offset < _src + _delta) {
364     _delta += count;
365     enhancer.setShort(_src + 1, _delta);
366       }
367       // offset is inside a backward jump
368
else if (_src + _delta <= offset && offset <= _src) {
369     _delta -= count;
370     enhancer.setShort(_src + 1, _delta);
371     _src += count;
372       }
373     }
374
375     void remove(CodeEnhancer enhancer, int offset, int count)
376     {
377       // offset is before the jump
378
if (offset <= _src && offset <= _src + _delta) {
379     _src -= count;
380       }
381       // offset is inside a forward jump
382
else if (_src < offset && offset < _src + _delta) {
383     _delta -= count;
384     enhancer.setShort(_src + 1, _delta);
385       }
386       // offset is inside a backward jump
387
else if (_src + _delta <= offset && offset <= _src) {
388     _delta += count;
389     enhancer.setShort(_src + 1, _delta);
390     _src -= count;
391       }
392     }
393   }
394
395   static class Switch extends Branch {
396     private int _oldSrc;
397     private int _src;
398     private int []_offsets;
399     
400     Switch(int src)
401     {
402       _src = src;
403       _oldSrc = src;
404     }
405
406     protected void setOffsets(int []offsets)
407     {
408       _offsets = offsets;
409     }
410
411     void insert(CodeEnhancer enhancer, int offset, int count)
412     {
413       for (int i = 0; i < _offsets.length; i++) {
414     int delta = enhancer.getInt(_offsets[i]);
415     
416     if (offset <= _src && _src + delta <= offset)
417       enhancer.setInt(_offsets[i], delta - count);
418     else if (_src < offset && offset < _src + delta)
419       enhancer.setInt(_offsets[i], delta + count);
420
421     if (offset <= _src + 1)
422       _offsets[i] += count;
423       }
424       
425       if (offset < _src)
426     _src += count;
427     }
428
429     void remove(CodeEnhancer enhancer, int offset, int count)
430     {
431       for (int i = 0; i < _offsets.length; i++) {
432     int delta = enhancer.getInt(_offsets[i]);
433     
434     if (offset <= _src && _src + delta <= offset)
435       enhancer.setInt(_offsets[i], delta + count);
436     else if (_src < offset && offset < _src + delta)
437       enhancer.setInt(_offsets[i], delta - count);
438     
439     if (offset <= _src + 1)
440       _offsets[i] -= count;
441       }
442       
443       if (offset < _src)
444     _src -= count;
445     }
446
447     void insertPad(CodeEnhancer enhancer, int offset, int count)
448     {
449       // offset is before the jump
450
if (_oldSrc != _src) {
451     int oldPad = (4 - (_oldSrc + 1) % 4) % 4;
452     int newPad = (4 - (_src + 1) % 4) % 4;
453
454     _oldSrc = _src;
455     
456     if (newPad < oldPad)
457       enhancer.remove(_src + 1, oldPad - newPad);
458     else if (oldPad < newPad)
459       enhancer.addNulls(_src + 1, newPad - oldPad);
460       }
461     }
462
463     void removePad(CodeEnhancer enhancer, int offset, int count)
464     {
465       // offset is before the jump
466
if (_oldSrc != _src) {
467     int oldPad = (4 - (_oldSrc + 1) % 4) % 4;
468     int newPad = (4 - (_src + 1) % 4) % 4;
469
470     _oldSrc = _src;
471     
472     if (newPad < oldPad)
473       enhancer.remove(_src + 1, oldPad - newPad);
474     else if (oldPad < newPad)
475       enhancer.addNulls(_src + 1, newPad - oldPad);
476       }
477     }
478
479     public boolean equals(Object JavaDoc v)
480     {
481       if (! (v instanceof Switch))
482     return false;
483       
484       Switch s = (Switch) v;
485
486       return _src == s._src;
487     }
488   }
489
490   static class TableSwitch extends Switch {
491     TableSwitch(int src, CodeVisitor visitor)
492     {
493       super(src);
494       
495       int arg = src + 1;
496       arg += (4 - arg % 4) % 4;
497     
498       int low = visitor.getInt(arg + 4);
499       int high = visitor.getInt(arg + 8);
500
501       int []offsets = new int[high - low + 2];
502
503       offsets[0] = arg;
504     
505       for (int i = 0; i <= high - low; i++) {
506     offsets[i + 1] = arg + 12 + i * 4;
507       }
508
509       setOffsets(offsets);
510     }
511   }
512
513   static class LookupSwitch extends Switch {
514     LookupSwitch(int src, CodeVisitor visitor)
515     {
516       super(src);
517       
518       int arg = src + 1;
519       arg += (4 - arg % 4) % 4;
520
521       int n = visitor.getInt(arg + 4);
522
523       int []offsets = new int[n + 1];
524       offsets[0] = arg;
525     
526       for (int i = 0; i < n; i++) {
527     offsets[i + 1] = arg + 8 + i * 8 + 4;
528       }
529
530       setOffsets(offsets);
531     }
532   }
533
534   class JumpAnalyzer extends Analyzer {
535     public void analyze(CodeVisitor visitor)
536       throws Exception JavaDoc
537     {
538       if (visitor.isSwitch()) {
539     int src = visitor.getOffset();
540
541     switch (visitor.getOpcode()) {
542     case TABLESWITCH:
543       {
544         TableSwitch branch = new TableSwitch(src, visitor);
545         if (! _switches.contains(branch))
546           _switches.add(branch);
547         break;
548       }
549       
550     case LOOKUPSWITCH:
551       {
552         LookupSwitch branch = new LookupSwitch(src, visitor);
553         if (! _switches.contains(branch))
554           _switches.add(branch);
555         break;
556       }
557     }
558       }
559       else if (visitor.isBranch()) {
560     int src = visitor.getOffset();
561     int offset = visitor.getShortArg(1);
562
563     _jumps.add(new Jump(src, offset));
564       }
565     }
566   }
567 }
568
Popular Tags